roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1241         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1242         + "if (results && results.length > 0) {";
1243     var regex = "";
1244
1245     var special = false;
1246     var ch = '';
1247     for (var i = 0; i < format.length; ++i) {
1248         ch = format.charAt(i);
1249         if (!special && ch == "\\") {
1250             special = true;
1251         }
1252         else if (special) {
1253             special = false;
1254             regex += String.escape(ch);
1255         }
1256         else {
1257             var obj = Date.formatCodeToRegex(ch, currentGroup);
1258             currentGroup += obj.g;
1259             regex += obj.s;
1260             if (obj.g && obj.c) {
1261                 code += obj.c;
1262             }
1263         }
1264     }
1265
1266     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1267         + "{v = new Date(y, m, d, h, i, s);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1269         + "{v = new Date(y, m, d, h, i);}\n"
1270         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1271         + "{v = new Date(y, m, d, h);}\n"
1272         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1273         + "{v = new Date(y, m, d);}\n"
1274         + "else if (y >= 0 && m >= 0)\n"
1275         + "{v = new Date(y, m);}\n"
1276         + "else if (y >= 0)\n"
1277         + "{v = new Date(y);}\n"
1278         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1279         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1280         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1281         + ";}";
1282
1283     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1284     /** eval:var:zzzzzzzzzzzzz */
1285     eval(code);
1286 };
1287
1288 // private
1289 Date.formatCodeToRegex = function(character, currentGroup) {
1290     switch (character) {
1291     case "D":
1292         return {g:0,
1293         c:null,
1294         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1295     case "j":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{1,2})"}; // day of month without leading zeroes
1299     case "d":
1300         return {g:1,
1301             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"}; // day of month with leading zeroes
1303     case "l":
1304         return {g:0,
1305             c:null,
1306             s:"(?:" + Date.dayNames.join("|") + ")"};
1307     case "S":
1308         return {g:0,
1309             c:null,
1310             s:"(?:st|nd|rd|th)"};
1311     case "w":
1312         return {g:0,
1313             c:null,
1314             s:"\\d"};
1315     case "z":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{1,3})"};
1319     case "W":
1320         return {g:0,
1321             c:null,
1322             s:"(?:\\d{2})"};
1323     case "F":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1326             s:"(" + Date.monthNames.join("|") + ")"};
1327     case "M":
1328         return {g:1,
1329             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1330             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1331     case "n":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1335     case "m":
1336         return {g:1,
1337             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1338             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1339     case "t":
1340         return {g:0,
1341             c:null,
1342             s:"\\d{1,2}"};
1343     case "L":
1344         return {g:0,
1345             c:null,
1346             s:"(?:1|0)"};
1347     case "Y":
1348         return {g:1,
1349             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1350             s:"(\\d{4})"};
1351     case "y":
1352         return {g:1,
1353             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1354                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1355             s:"(\\d{1,2})"};
1356     case "a":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'am') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(am|pm)"};
1362     case "A":
1363         return {g:1,
1364             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1365                 + "if (h == 12) { h = 0; }\n"
1366                 + "} else { if (h < 12) { h += 12; }}",
1367             s:"(AM|PM)"};
1368     case "g":
1369     case "G":
1370         return {g:1,
1371             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1373     case "h":
1374     case "H":
1375         return {g:1,
1376             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1378     case "i":
1379         return {g:1,
1380             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "s":
1383         return {g:1,
1384             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1385             s:"(\\d{2})"};
1386     case "O":
1387         return {g:1,
1388             c:[
1389                 "o = results[", currentGroup, "];\n",
1390                 "var sn = o.substring(0,1);\n", // get + / - sign
1391                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1392                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1393                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1394                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1395             ].join(""),
1396             s:"([+\-]\\d{2,4})"};
1397     
1398     
1399     case "P":
1400         return {g:1,
1401                 c:[
1402                    "o = results[", currentGroup, "];\n",
1403                    "var sn = o.substring(0,1);\n",
1404                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1405                    "var mn = o.substring(4,6) % 60;\n",
1406                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1407                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1408             ].join(""),
1409             s:"([+\-]\\d{4})"};
1410     case "T":
1411         return {g:0,
1412             c:null,
1413             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1414     case "Z":
1415         return {g:1,
1416             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1417                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1418             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1419     default:
1420         return {g:0,
1421             c:null,
1422             s:String.escape(character)};
1423     }
1424 };
1425
1426 /**
1427  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1428  * @return {String} The abbreviated timezone name (e.g. 'CST')
1429  */
1430 Date.prototype.getTimezone = function() {
1431     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1432 };
1433
1434 /**
1435  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1436  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1437  */
1438 Date.prototype.getGMTOffset = function() {
1439     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1440         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1441         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1442 };
1443
1444 /**
1445  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1446  * @return {String} 2-characters representing hours and 2-characters representing minutes
1447  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1448  */
1449 Date.prototype.getGMTColonOffset = function() {
1450         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1451                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1452                 + ":"
1453                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1454 }
1455
1456 /**
1457  * Get the numeric day number of the year, adjusted for leap year.
1458  * @return {Number} 0 through 364 (365 in leap years)
1459  */
1460 Date.prototype.getDayOfYear = function() {
1461     var num = 0;
1462     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1463     for (var i = 0; i < this.getMonth(); ++i) {
1464         num += Date.daysInMonth[i];
1465     }
1466     return num + this.getDate() - 1;
1467 };
1468
1469 /**
1470  * Get the string representation of the numeric week number of the year
1471  * (equivalent to the format specifier 'W').
1472  * @return {String} '00' through '52'
1473  */
1474 Date.prototype.getWeekOfYear = function() {
1475     // Skip to Thursday of this week
1476     var now = this.getDayOfYear() + (4 - this.getDay());
1477     // Find the first Thursday of the year
1478     var jan1 = new Date(this.getFullYear(), 0, 1);
1479     var then = (7 - jan1.getDay() + 4);
1480     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1481 };
1482
1483 /**
1484  * Whether or not the current date is in a leap year.
1485  * @return {Boolean} True if the current date is in a leap year, else false
1486  */
1487 Date.prototype.isLeapYear = function() {
1488     var year = this.getFullYear();
1489     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1490 };
1491
1492 /**
1493  * Get the first day of the current month, adjusted for leap year.  The returned value
1494  * is the numeric day index within the week (0-6) which can be used in conjunction with
1495  * the {@link #monthNames} array to retrieve the textual day name.
1496  * Example:
1497  *<pre><code>
1498 var dt = new Date('1/10/2007');
1499 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1500 </code></pre>
1501  * @return {Number} The day number (0-6)
1502  */
1503 Date.prototype.getFirstDayOfMonth = function() {
1504     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1505     return (day < 0) ? (day + 7) : day;
1506 };
1507
1508 /**
1509  * Get the last day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getLastDayOfMonth = function() {
1520     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524
1525 /**
1526  * Get the first date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getFirstDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), 1);
1531 };
1532
1533 /**
1534  * Get the last date of this date's month
1535  * @return {Date}
1536  */
1537 Date.prototype.getLastDateOfMonth = function() {
1538     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1539 };
1540 /**
1541  * Get the number of days in the current month, adjusted for leap year.
1542  * @return {Number} The number of days in the month
1543  */
1544 Date.prototype.getDaysInMonth = function() {
1545     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1546     return Date.daysInMonth[this.getMonth()];
1547 };
1548
1549 /**
1550  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1551  * @return {String} 'st, 'nd', 'rd' or 'th'
1552  */
1553 Date.prototype.getSuffix = function() {
1554     switch (this.getDate()) {
1555         case 1:
1556         case 21:
1557         case 31:
1558             return "st";
1559         case 2:
1560         case 22:
1561             return "nd";
1562         case 3:
1563         case 23:
1564             return "rd";
1565         default:
1566             return "th";
1567     }
1568 };
1569
1570 // private
1571 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1572
1573 /**
1574  * An array of textual month names.
1575  * Override these values for international dates, for example...
1576  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1577  * @type Array
1578  * @static
1579  */
1580 Date.monthNames =
1581    ["January",
1582     "February",
1583     "March",
1584     "April",
1585     "May",
1586     "June",
1587     "July",
1588     "August",
1589     "September",
1590     "October",
1591     "November",
1592     "December"];
1593
1594 /**
1595  * An array of textual day names.
1596  * Override these values for international dates, for example...
1597  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.dayNames =
1602    ["Sunday",
1603     "Monday",
1604     "Tuesday",
1605     "Wednesday",
1606     "Thursday",
1607     "Friday",
1608     "Saturday"];
1609
1610 // private
1611 Date.y2kYear = 50;
1612 // private
1613 Date.monthNumbers = {
1614     Jan:0,
1615     Feb:1,
1616     Mar:2,
1617     Apr:3,
1618     May:4,
1619     Jun:5,
1620     Jul:6,
1621     Aug:7,
1622     Sep:8,
1623     Oct:9,
1624     Nov:10,
1625     Dec:11};
1626
1627 /**
1628  * Creates and returns a new Date instance with the exact same date value as the called instance.
1629  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1630  * variable will also be changed.  When the intention is to create a new variable that will not
1631  * modify the original instance, you should create a clone.
1632  *
1633  * Example of correctly cloning a date:
1634  * <pre><code>
1635 //wrong way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig;
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 05 2006'!
1640
1641 //correct way:
1642 var orig = new Date('10/1/2006');
1643 var copy = orig.clone();
1644 copy.setDate(5);
1645 document.write(orig);  //returns 'Thu Oct 01 2006'
1646 </code></pre>
1647  * @return {Date} The new Date instance
1648  */
1649 Date.prototype.clone = function() {
1650         return new Date(this.getTime());
1651 };
1652
1653 /**
1654  * Clears any time information from this date
1655  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1656  @return {Date} this or the clone
1657  */
1658 Date.prototype.clearTime = function(clone){
1659     if(clone){
1660         return this.clone().clearTime();
1661     }
1662     this.setHours(0);
1663     this.setMinutes(0);
1664     this.setSeconds(0);
1665     this.setMilliseconds(0);
1666     return this;
1667 };
1668
1669 // private
1670 // safari setMonth is broken
1671 if(Roo.isSafari){
1672     Date.brokenSetMonth = Date.prototype.setMonth;
1673         Date.prototype.setMonth = function(num){
1674                 if(num <= -1){
1675                         var n = Math.ceil(-num);
1676                         var back_year = Math.ceil(n/12);
1677                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1678                         this.setFullYear(this.getFullYear() - back_year);
1679                         return Date.brokenSetMonth.call(this, month);
1680                 } else {
1681                         return Date.brokenSetMonth.apply(this, arguments);
1682                 }
1683         };
1684 }
1685
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MILLI = "ms";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.SECOND = "s";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.MINUTE = "mi";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.HOUR = "h";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.DAY = "d";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.MONTH = "mo";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.YEAR = "y";
1714
1715 /**
1716  * Provides a convenient method of performing basic date arithmetic.  This method
1717  * does not modify the Date instance being called - it creates and returns
1718  * a new Date instance containing the resulting date value.
1719  *
1720  * Examples:
1721  * <pre><code>
1722 //Basic usage:
1723 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1724 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1725
1726 //Negative values will subtract correctly:
1727 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1728 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1729
1730 //You can even chain several calls together in one line!
1731 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1732 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1733  </code></pre>
1734  *
1735  * @param {String} interval   A valid date interval enum value
1736  * @param {Number} value      The amount to add to the current date
1737  * @return {Date} The new Date instance
1738  */
1739 Date.prototype.add = function(interval, value){
1740   var d = this.clone();
1741   if (!interval || value === 0) return d;
1742   switch(interval.toLowerCase()){
1743     case Date.MILLI:
1744       d.setMilliseconds(this.getMilliseconds() + value);
1745       break;
1746     case Date.SECOND:
1747       d.setSeconds(this.getSeconds() + value);
1748       break;
1749     case Date.MINUTE:
1750       d.setMinutes(this.getMinutes() + value);
1751       break;
1752     case Date.HOUR:
1753       d.setHours(this.getHours() + value);
1754       break;
1755     case Date.DAY:
1756       d.setDate(this.getDate() + value);
1757       break;
1758     case Date.MONTH:
1759       var day = this.getDate();
1760       if(day > 28){
1761           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1762       }
1763       d.setDate(day);
1764       d.setMonth(this.getMonth() + value);
1765       break;
1766     case Date.YEAR:
1767       d.setFullYear(this.getFullYear() + value);
1768       break;
1769   }
1770   return d;
1771 };
1772 /*
1773  * Based on:
1774  * Ext JS Library 1.1.1
1775  * Copyright(c) 2006-2007, Ext JS, LLC.
1776  *
1777  * Originally Released Under LGPL - original licence link has changed is not relivant.
1778  *
1779  * Fork - LGPL
1780  * <script type="text/javascript">
1781  */
1782
1783 /**
1784  * @class Roo.lib.Dom
1785  * @static
1786  * 
1787  * Dom utils (from YIU afaik)
1788  * 
1789  **/
1790 Roo.lib.Dom = {
1791     /**
1792      * Get the view width
1793      * @param {Boolean} full True will get the full document, otherwise it's the view width
1794      * @return {Number} The width
1795      */
1796      
1797     getViewWidth : function(full) {
1798         return full ? this.getDocumentWidth() : this.getViewportWidth();
1799     },
1800     /**
1801      * Get the view height
1802      * @param {Boolean} full True will get the full document, otherwise it's the view height
1803      * @return {Number} The height
1804      */
1805     getViewHeight : function(full) {
1806         return full ? this.getDocumentHeight() : this.getViewportHeight();
1807     },
1808
1809     getDocumentHeight: function() {
1810         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1811         return Math.max(scrollHeight, this.getViewportHeight());
1812     },
1813
1814     getDocumentWidth: function() {
1815         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1816         return Math.max(scrollWidth, this.getViewportWidth());
1817     },
1818
1819     getViewportHeight: function() {
1820         var height = self.innerHeight;
1821         var mode = document.compatMode;
1822
1823         if ((mode || Roo.isIE) && !Roo.isOpera) {
1824             height = (mode == "CSS1Compat") ?
1825                      document.documentElement.clientHeight :
1826                      document.body.clientHeight;
1827         }
1828
1829         return height;
1830     },
1831
1832     getViewportWidth: function() {
1833         var width = self.innerWidth;
1834         var mode = document.compatMode;
1835
1836         if (mode || Roo.isIE) {
1837             width = (mode == "CSS1Compat") ?
1838                     document.documentElement.clientWidth :
1839                     document.body.clientWidth;
1840         }
1841         return width;
1842     },
1843
1844     isAncestor : function(p, c) {
1845         p = Roo.getDom(p);
1846         c = Roo.getDom(c);
1847         if (!p || !c) {
1848             return false;
1849         }
1850
1851         if (p.contains && !Roo.isSafari) {
1852             return p.contains(c);
1853         } else if (p.compareDocumentPosition) {
1854             return !!(p.compareDocumentPosition(c) & 16);
1855         } else {
1856             var parent = c.parentNode;
1857             while (parent) {
1858                 if (parent == p) {
1859                     return true;
1860                 }
1861                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1862                     return false;
1863                 }
1864                 parent = parent.parentNode;
1865             }
1866             return false;
1867         }
1868     },
1869
1870     getRegion : function(el) {
1871         return Roo.lib.Region.getRegion(el);
1872     },
1873
1874     getY : function(el) {
1875         return this.getXY(el)[1];
1876     },
1877
1878     getX : function(el) {
1879         return this.getXY(el)[0];
1880     },
1881
1882     getXY : function(el) {
1883         var p, pe, b, scroll, bd = document.body;
1884         el = Roo.getDom(el);
1885         var fly = Roo.lib.AnimBase.fly;
1886         if (el.getBoundingClientRect) {
1887             b = el.getBoundingClientRect();
1888             scroll = fly(document).getScroll();
1889             return [b.left + scroll.left, b.top + scroll.top];
1890         }
1891         var x = 0, y = 0;
1892
1893         p = el;
1894
1895         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1896
1897         while (p) {
1898
1899             x += p.offsetLeft;
1900             y += p.offsetTop;
1901
1902             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1903                 hasAbsolute = true;
1904             }
1905
1906             if (Roo.isGecko) {
1907                 pe = fly(p);
1908
1909                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1910                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1911
1912
1913                 x += bl;
1914                 y += bt;
1915
1916
1917                 if (p != el && pe.getStyle('overflow') != 'visible') {
1918                     x += bl;
1919                     y += bt;
1920                 }
1921             }
1922             p = p.offsetParent;
1923         }
1924
1925         if (Roo.isSafari && hasAbsolute) {
1926             x -= bd.offsetLeft;
1927             y -= bd.offsetTop;
1928         }
1929
1930         if (Roo.isGecko && !hasAbsolute) {
1931             var dbd = fly(bd);
1932             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1933             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1934         }
1935
1936         p = el.parentNode;
1937         while (p && p != bd) {
1938             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1939                 x -= p.scrollLeft;
1940                 y -= p.scrollTop;
1941             }
1942             p = p.parentNode;
1943         }
1944         return [x, y];
1945     },
1946  
1947   
1948
1949
1950     setXY : function(el, xy) {
1951         el = Roo.fly(el, '_setXY');
1952         el.position();
1953         var pts = el.translatePoints(xy);
1954         if (xy[0] !== false) {
1955             el.dom.style.left = pts.left + "px";
1956         }
1957         if (xy[1] !== false) {
1958             el.dom.style.top = pts.top + "px";
1959         }
1960     },
1961
1962     setX : function(el, x) {
1963         this.setXY(el, [x, false]);
1964     },
1965
1966     setY : function(el, y) {
1967         this.setXY(el, [false, y]);
1968     }
1969 };
1970 /*
1971  * Portions of this file are based on pieces of Yahoo User Interface Library
1972  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1973  * YUI licensed under the BSD License:
1974  * http://developer.yahoo.net/yui/license.txt
1975  * <script type="text/javascript">
1976  *
1977  */
1978
1979 Roo.lib.Event = function() {
1980     var loadComplete = false;
1981     var listeners = [];
1982     var unloadListeners = [];
1983     var retryCount = 0;
1984     var onAvailStack = [];
1985     var counter = 0;
1986     var lastError = null;
1987
1988     return {
1989         POLL_RETRYS: 200,
1990         POLL_INTERVAL: 20,
1991         EL: 0,
1992         TYPE: 1,
1993         FN: 2,
1994         WFN: 3,
1995         OBJ: 3,
1996         ADJ_SCOPE: 4,
1997         _interval: null,
1998
1999         startInterval: function() {
2000             if (!this._interval) {
2001                 var self = this;
2002                 var callback = function() {
2003                     self._tryPreloadAttach();
2004                 };
2005                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2006
2007             }
2008         },
2009
2010         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2011             onAvailStack.push({ id:         p_id,
2012                 fn:         p_fn,
2013                 obj:        p_obj,
2014                 override:   p_override,
2015                 checkReady: false    });
2016
2017             retryCount = this.POLL_RETRYS;
2018             this.startInterval();
2019         },
2020
2021
2022         addListener: function(el, eventName, fn) {
2023             el = Roo.getDom(el);
2024             if (!el || !fn) {
2025                 return false;
2026             }
2027
2028             if ("unload" == eventName) {
2029                 unloadListeners[unloadListeners.length] =
2030                 [el, eventName, fn];
2031                 return true;
2032             }
2033
2034             var wrappedFn = function(e) {
2035                 return fn(Roo.lib.Event.getEvent(e));
2036             };
2037
2038             var li = [el, eventName, fn, wrappedFn];
2039
2040             var index = listeners.length;
2041             listeners[index] = li;
2042
2043             this.doAdd(el, eventName, wrappedFn, false);
2044             return true;
2045
2046         },
2047
2048
2049         removeListener: function(el, eventName, fn) {
2050             var i, len;
2051
2052             el = Roo.getDom(el);
2053
2054             if(!fn) {
2055                 return this.purgeElement(el, false, eventName);
2056             }
2057
2058
2059             if ("unload" == eventName) {
2060
2061                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2062                     var li = unloadListeners[i];
2063                     if (li &&
2064                         li[0] == el &&
2065                         li[1] == eventName &&
2066                         li[2] == fn) {
2067                         unloadListeners.splice(i, 1);
2068                         return true;
2069                     }
2070                 }
2071
2072                 return false;
2073             }
2074
2075             var cacheItem = null;
2076
2077
2078             var index = arguments[3];
2079
2080             if ("undefined" == typeof index) {
2081                 index = this._getCacheIndex(el, eventName, fn);
2082             }
2083
2084             if (index >= 0) {
2085                 cacheItem = listeners[index];
2086             }
2087
2088             if (!el || !cacheItem) {
2089                 return false;
2090             }
2091
2092             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2093
2094             delete listeners[index][this.WFN];
2095             delete listeners[index][this.FN];
2096             listeners.splice(index, 1);
2097
2098             return true;
2099
2100         },
2101
2102
2103         getTarget: function(ev, resolveTextNode) {
2104             ev = ev.browserEvent || ev;
2105             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2106             var t = ev.target || ev.srcElement;
2107             return this.resolveTextNode(t);
2108         },
2109
2110
2111         resolveTextNode: function(node) {
2112             if (Roo.isSafari && node && 3 == node.nodeType) {
2113                 return node.parentNode;
2114             } else {
2115                 return node;
2116             }
2117         },
2118
2119
2120         getPageX: function(ev) {
2121             ev = ev.browserEvent || ev;
2122             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2123             var x = ev.pageX;
2124             if (!x && 0 !== x) {
2125                 x = ev.clientX || 0;
2126
2127                 if (Roo.isIE) {
2128                     x += this.getScroll()[1];
2129                 }
2130             }
2131
2132             return x;
2133         },
2134
2135
2136         getPageY: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var y = ev.pageY;
2140             if (!y && 0 !== y) {
2141                 y = ev.clientY || 0;
2142
2143                 if (Roo.isIE) {
2144                     y += this.getScroll()[0];
2145                 }
2146             }
2147
2148
2149             return y;
2150         },
2151
2152
2153         getXY: function(ev) {
2154             ev = ev.browserEvent || ev;
2155             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2156             return [this.getPageX(ev), this.getPageY(ev)];
2157         },
2158
2159
2160         getRelatedTarget: function(ev) {
2161             ev = ev.browserEvent || ev;
2162             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2163             var t = ev.relatedTarget;
2164             if (!t) {
2165                 if (ev.type == "mouseout") {
2166                     t = ev.toElement;
2167                 } else if (ev.type == "mouseover") {
2168                     t = ev.fromElement;
2169                 }
2170             }
2171
2172             return this.resolveTextNode(t);
2173         },
2174
2175
2176         getTime: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             if (!ev.time) {
2180                 var t = new Date().getTime();
2181                 try {
2182                     ev.time = t;
2183                 } catch(ex) {
2184                     this.lastError = ex;
2185                     return t;
2186                 }
2187             }
2188
2189             return ev.time;
2190         },
2191
2192
2193         stopEvent: function(ev) {
2194             this.stopPropagation(ev);
2195             this.preventDefault(ev);
2196         },
2197
2198
2199         stopPropagation: function(ev) {
2200             ev = ev.browserEvent || ev;
2201             if (ev.stopPropagation) {
2202                 ev.stopPropagation();
2203             } else {
2204                 ev.cancelBubble = true;
2205             }
2206         },
2207
2208
2209         preventDefault: function(ev) {
2210             ev = ev.browserEvent || ev;
2211             if(ev.preventDefault) {
2212                 ev.preventDefault();
2213             } else {
2214                 ev.returnValue = false;
2215             }
2216         },
2217
2218
2219         getEvent: function(e) {
2220             var ev = e || window.event;
2221             if (!ev) {
2222                 var c = this.getEvent.caller;
2223                 while (c) {
2224                     ev = c.arguments[0];
2225                     if (ev && Event == ev.constructor) {
2226                         break;
2227                     }
2228                     c = c.caller;
2229                 }
2230             }
2231             return ev;
2232         },
2233
2234
2235         getCharCode: function(ev) {
2236             ev = ev.browserEvent || ev;
2237             return ev.charCode || ev.keyCode || 0;
2238         },
2239
2240
2241         _getCacheIndex: function(el, eventName, fn) {
2242             for (var i = 0,len = listeners.length; i < len; ++i) {
2243                 var li = listeners[i];
2244                 if (li &&
2245                     li[this.FN] == fn &&
2246                     li[this.EL] == el &&
2247                     li[this.TYPE] == eventName) {
2248                     return i;
2249                 }
2250             }
2251
2252             return -1;
2253         },
2254
2255
2256         elCache: {},
2257
2258
2259         getEl: function(id) {
2260             return document.getElementById(id);
2261         },
2262
2263
2264         clearCache: function() {
2265         },
2266
2267
2268         _load: function(e) {
2269             loadComplete = true;
2270             var EU = Roo.lib.Event;
2271
2272
2273             if (Roo.isIE) {
2274                 EU.doRemove(window, "load", EU._load);
2275             }
2276         },
2277
2278
2279         _tryPreloadAttach: function() {
2280
2281             if (this.locked) {
2282                 return false;
2283             }
2284
2285             this.locked = true;
2286
2287
2288             var tryAgain = !loadComplete;
2289             if (!tryAgain) {
2290                 tryAgain = (retryCount > 0);
2291             }
2292
2293
2294             var notAvail = [];
2295             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2296                 var item = onAvailStack[i];
2297                 if (item) {
2298                     var el = this.getEl(item.id);
2299
2300                     if (el) {
2301                         if (!item.checkReady ||
2302                             loadComplete ||
2303                             el.nextSibling ||
2304                             (document && document.body)) {
2305
2306                             var scope = el;
2307                             if (item.override) {
2308                                 if (item.override === true) {
2309                                     scope = item.obj;
2310                                 } else {
2311                                     scope = item.override;
2312                                 }
2313                             }
2314                             item.fn.call(scope, item.obj);
2315                             onAvailStack[i] = null;
2316                         }
2317                     } else {
2318                         notAvail.push(item);
2319                     }
2320                 }
2321             }
2322
2323             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2324
2325             if (tryAgain) {
2326
2327                 this.startInterval();
2328             } else {
2329                 clearInterval(this._interval);
2330                 this._interval = null;
2331             }
2332
2333             this.locked = false;
2334
2335             return true;
2336
2337         },
2338
2339
2340         purgeElement: function(el, recurse, eventName) {
2341             var elListeners = this.getListeners(el, eventName);
2342             if (elListeners) {
2343                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2344                     var l = elListeners[i];
2345                     this.removeListener(el, l.type, l.fn);
2346                 }
2347             }
2348
2349             if (recurse && el && el.childNodes) {
2350                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2351                     this.purgeElement(el.childNodes[i], recurse, eventName);
2352                 }
2353             }
2354         },
2355
2356
2357         getListeners: function(el, eventName) {
2358             var results = [], searchLists;
2359             if (!eventName) {
2360                 searchLists = [listeners, unloadListeners];
2361             } else if (eventName == "unload") {
2362                 searchLists = [unloadListeners];
2363             } else {
2364                 searchLists = [listeners];
2365             }
2366
2367             for (var j = 0; j < searchLists.length; ++j) {
2368                 var searchList = searchLists[j];
2369                 if (searchList && searchList.length > 0) {
2370                     for (var i = 0,len = searchList.length; i < len; ++i) {
2371                         var l = searchList[i];
2372                         if (l && l[this.EL] === el &&
2373                             (!eventName || eventName === l[this.TYPE])) {
2374                             results.push({
2375                                 type:   l[this.TYPE],
2376                                 fn:     l[this.FN],
2377                                 obj:    l[this.OBJ],
2378                                 adjust: l[this.ADJ_SCOPE],
2379                                 index:  i
2380                             });
2381                         }
2382                     }
2383                 }
2384             }
2385
2386             return (results.length) ? results : null;
2387         },
2388
2389
2390         _unload: function(e) {
2391
2392             var EU = Roo.lib.Event, i, j, l, len, index;
2393
2394             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2395                 l = unloadListeners[i];
2396                 if (l) {
2397                     var scope = window;
2398                     if (l[EU.ADJ_SCOPE]) {
2399                         if (l[EU.ADJ_SCOPE] === true) {
2400                             scope = l[EU.OBJ];
2401                         } else {
2402                             scope = l[EU.ADJ_SCOPE];
2403                         }
2404                     }
2405                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2406                     unloadListeners[i] = null;
2407                     l = null;
2408                     scope = null;
2409                 }
2410             }
2411
2412             unloadListeners = null;
2413
2414             if (listeners && listeners.length > 0) {
2415                 j = listeners.length;
2416                 while (j) {
2417                     index = j - 1;
2418                     l = listeners[index];
2419                     if (l) {
2420                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2421                                 l[EU.FN], index);
2422                     }
2423                     j = j - 1;
2424                 }
2425                 l = null;
2426
2427                 EU.clearCache();
2428             }
2429
2430             EU.doRemove(window, "unload", EU._unload);
2431
2432         },
2433
2434
2435         getScroll: function() {
2436             var dd = document.documentElement, db = document.body;
2437             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2438                 return [dd.scrollTop, dd.scrollLeft];
2439             } else if (db) {
2440                 return [db.scrollTop, db.scrollLeft];
2441             } else {
2442                 return [0, 0];
2443             }
2444         },
2445
2446
2447         doAdd: function () {
2448             if (window.addEventListener) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.addEventListener(eventName, fn, (capture));
2451                 };
2452             } else if (window.attachEvent) {
2453                 return function(el, eventName, fn, capture) {
2454                     el.attachEvent("on" + eventName, fn);
2455                 };
2456             } else {
2457                 return function() {
2458                 };
2459             }
2460         }(),
2461
2462
2463         doRemove: function() {
2464             if (window.removeEventListener) {
2465                 return function (el, eventName, fn, capture) {
2466                     el.removeEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.detachEvent) {
2469                 return function (el, eventName, fn) {
2470                     el.detachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }()
2477     };
2478     
2479 }();
2480 (function() {     
2481    
2482     var E = Roo.lib.Event;
2483     E.on = E.addListener;
2484     E.un = E.removeListener;
2485
2486     if (document && document.body) {
2487         E._load();
2488     } else {
2489         E.doAdd(window, "load", E._load);
2490     }
2491     E.doAdd(window, "unload", E._unload);
2492     E._tryPreloadAttach();
2493 })();
2494
2495 /*
2496  * Portions of this file are based on pieces of Yahoo User Interface Library
2497  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2498  * YUI licensed under the BSD License:
2499  * http://developer.yahoo.net/yui/license.txt
2500  * <script type="text/javascript">
2501  *
2502  */
2503
2504 (function() {
2505     /**
2506      * @class Roo.lib.Ajax
2507      *
2508      */
2509     Roo.lib.Ajax = {
2510         /**
2511          * @static 
2512          */
2513         request : function(method, uri, cb, data, options) {
2514             if(options){
2515                 var hs = options.headers;
2516                 if(hs){
2517                     for(var h in hs){
2518                         if(hs.hasOwnProperty(h)){
2519                             this.initHeader(h, hs[h], false);
2520                         }
2521                     }
2522                 }
2523                 if(options.xmlData){
2524                     this.initHeader('Content-Type', 'text/xml', false);
2525                     method = 'POST';
2526                     data = options.xmlData;
2527                 }
2528             }
2529
2530             return this.asyncRequest(method, uri, cb, data);
2531         },
2532
2533         serializeForm : function(form) {
2534             if(typeof form == 'string') {
2535                 form = (document.getElementById(form) || document.forms[form]);
2536             }
2537
2538             var el, name, val, disabled, data = '', hasSubmit = false;
2539             for (var i = 0; i < form.elements.length; i++) {
2540                 el = form.elements[i];
2541                 disabled = form.elements[i].disabled;
2542                 name = form.elements[i].name;
2543                 val = form.elements[i].value;
2544
2545                 if (!disabled && name){
2546                     switch (el.type)
2547                             {
2548                         case 'select-one':
2549                         case 'select-multiple':
2550                             for (var j = 0; j < el.options.length; j++) {
2551                                 if (el.options[j].selected) {
2552                                     if (Roo.isIE) {
2553                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2554                                     }
2555                                     else {
2556                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2557                                     }
2558                                 }
2559                             }
2560                             break;
2561                         case 'radio':
2562                         case 'checkbox':
2563                             if (el.checked) {
2564                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2565                             }
2566                             break;
2567                         case 'file':
2568
2569                         case undefined:
2570
2571                         case 'reset':
2572
2573                         case 'button':
2574
2575                             break;
2576                         case 'submit':
2577                             if(hasSubmit == false) {
2578                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2579                                 hasSubmit = true;
2580                             }
2581                             break;
2582                         default:
2583                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             break;
2585                     }
2586                 }
2587             }
2588             data = data.substr(0, data.length - 1);
2589             return data;
2590         },
2591
2592         headers:{},
2593
2594         hasHeaders:false,
2595
2596         useDefaultHeader:true,
2597
2598         defaultPostHeader:'application/x-www-form-urlencoded',
2599
2600         useDefaultXhrHeader:true,
2601
2602         defaultXhrHeader:'XMLHttpRequest',
2603
2604         hasDefaultHeaders:true,
2605
2606         defaultHeaders:{},
2607
2608         poll:{},
2609
2610         timeout:{},
2611
2612         pollInterval:50,
2613
2614         transactionId:0,
2615
2616         setProgId:function(id)
2617         {
2618             this.activeX.unshift(id);
2619         },
2620
2621         setDefaultPostHeader:function(b)
2622         {
2623             this.useDefaultHeader = b;
2624         },
2625
2626         setDefaultXhrHeader:function(b)
2627         {
2628             this.useDefaultXhrHeader = b;
2629         },
2630
2631         setPollingInterval:function(i)
2632         {
2633             if (typeof i == 'number' && isFinite(i)) {
2634                 this.pollInterval = i;
2635             }
2636         },
2637
2638         createXhrObject:function(transactionId)
2639         {
2640             var obj,http;
2641             try
2642             {
2643
2644                 http = new XMLHttpRequest();
2645
2646                 obj = { conn:http, tId:transactionId };
2647             }
2648             catch(e)
2649             {
2650                 for (var i = 0; i < this.activeX.length; ++i) {
2651                     try
2652                     {
2653
2654                         http = new ActiveXObject(this.activeX[i]);
2655
2656                         obj = { conn:http, tId:transactionId };
2657                         break;
2658                     }
2659                     catch(e) {
2660                     }
2661                 }
2662             }
2663             finally
2664             {
2665                 return obj;
2666             }
2667         },
2668
2669         getConnectionObject:function()
2670         {
2671             var o;
2672             var tId = this.transactionId;
2673
2674             try
2675             {
2676                 o = this.createXhrObject(tId);
2677                 if (o) {
2678                     this.transactionId++;
2679                 }
2680             }
2681             catch(e) {
2682             }
2683             finally
2684             {
2685                 return o;
2686             }
2687         },
2688
2689         asyncRequest:function(method, uri, callback, postData)
2690         {
2691             var o = this.getConnectionObject();
2692
2693             if (!o) {
2694                 return null;
2695             }
2696             else {
2697                 o.conn.open(method, uri, true);
2698
2699                 if (this.useDefaultXhrHeader) {
2700                     if (!this.defaultHeaders['X-Requested-With']) {
2701                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2702                     }
2703                 }
2704
2705                 if(postData && this.useDefaultHeader){
2706                     this.initHeader('Content-Type', this.defaultPostHeader);
2707                 }
2708
2709                  if (this.hasDefaultHeaders || this.hasHeaders) {
2710                     this.setHeader(o);
2711                 }
2712
2713                 this.handleReadyState(o, callback);
2714                 o.conn.send(postData || null);
2715
2716                 return o;
2717             }
2718         },
2719
2720         handleReadyState:function(o, callback)
2721         {
2722             var oConn = this;
2723
2724             if (callback && callback.timeout) {
2725                 
2726                 this.timeout[o.tId] = window.setTimeout(function() {
2727                     oConn.abort(o, callback, true);
2728                 }, callback.timeout);
2729             }
2730
2731             this.poll[o.tId] = window.setInterval(
2732                     function() {
2733                         if (o.conn && o.conn.readyState == 4) {
2734                             window.clearInterval(oConn.poll[o.tId]);
2735                             delete oConn.poll[o.tId];
2736
2737                             if(callback && callback.timeout) {
2738                                 window.clearTimeout(oConn.timeout[o.tId]);
2739                                 delete oConn.timeout[o.tId];
2740                             }
2741
2742                             oConn.handleTransactionResponse(o, callback);
2743                         }
2744                     }
2745                     , this.pollInterval);
2746         },
2747
2748         handleTransactionResponse:function(o, callback, isAbort)
2749         {
2750
2751             if (!callback) {
2752                 this.releaseObject(o);
2753                 return;
2754             }
2755
2756             var httpStatus, responseObject;
2757
2758             try
2759             {
2760                 if (o.conn.status !== undefined && o.conn.status != 0) {
2761                     httpStatus = o.conn.status;
2762                 }
2763                 else {
2764                     httpStatus = 13030;
2765                 }
2766             }
2767             catch(e) {
2768
2769
2770                 httpStatus = 13030;
2771             }
2772
2773             if (httpStatus >= 200 && httpStatus < 300) {
2774                 responseObject = this.createResponseObject(o, callback.argument);
2775                 if (callback.success) {
2776                     if (!callback.scope) {
2777                         callback.success(responseObject);
2778                     }
2779                     else {
2780
2781
2782                         callback.success.apply(callback.scope, [responseObject]);
2783                     }
2784                 }
2785             }
2786             else {
2787                 switch (httpStatus) {
2788
2789                     case 12002:
2790                     case 12029:
2791                     case 12030:
2792                     case 12031:
2793                     case 12152:
2794                     case 13030:
2795                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2796                         if (callback.failure) {
2797                             if (!callback.scope) {
2798                                 callback.failure(responseObject);
2799                             }
2800                             else {
2801                                 callback.failure.apply(callback.scope, [responseObject]);
2802                             }
2803                         }
2804                         break;
2805                     default:
2806                         responseObject = this.createResponseObject(o, callback.argument);
2807                         if (callback.failure) {
2808                             if (!callback.scope) {
2809                                 callback.failure(responseObject);
2810                             }
2811                             else {
2812                                 callback.failure.apply(callback.scope, [responseObject]);
2813                             }
2814                         }
2815                 }
2816             }
2817
2818             this.releaseObject(o);
2819             responseObject = null;
2820         },
2821
2822         createResponseObject:function(o, callbackArg)
2823         {
2824             var obj = {};
2825             var headerObj = {};
2826
2827             try
2828             {
2829                 var headerStr = o.conn.getAllResponseHeaders();
2830                 var header = headerStr.split('\n');
2831                 for (var i = 0; i < header.length; i++) {
2832                     var delimitPos = header[i].indexOf(':');
2833                     if (delimitPos != -1) {
2834                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2835                     }
2836                 }
2837             }
2838             catch(e) {
2839             }
2840
2841             obj.tId = o.tId;
2842             obj.status = o.conn.status;
2843             obj.statusText = o.conn.statusText;
2844             obj.getResponseHeader = headerObj;
2845             obj.getAllResponseHeaders = headerStr;
2846             obj.responseText = o.conn.responseText;
2847             obj.responseXML = o.conn.responseXML;
2848
2849             if (typeof callbackArg !== undefined) {
2850                 obj.argument = callbackArg;
2851             }
2852
2853             return obj;
2854         },
2855
2856         createExceptionObject:function(tId, callbackArg, isAbort)
2857         {
2858             var COMM_CODE = 0;
2859             var COMM_ERROR = 'communication failure';
2860             var ABORT_CODE = -1;
2861             var ABORT_ERROR = 'transaction aborted';
2862
2863             var obj = {};
2864
2865             obj.tId = tId;
2866             if (isAbort) {
2867                 obj.status = ABORT_CODE;
2868                 obj.statusText = ABORT_ERROR;
2869             }
2870             else {
2871                 obj.status = COMM_CODE;
2872                 obj.statusText = COMM_ERROR;
2873             }
2874
2875             if (callbackArg) {
2876                 obj.argument = callbackArg;
2877             }
2878
2879             return obj;
2880         },
2881
2882         initHeader:function(label, value, isDefault)
2883         {
2884             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2885
2886             if (headerObj[label] === undefined) {
2887                 headerObj[label] = value;
2888             }
2889             else {
2890
2891
2892                 headerObj[label] = value + "," + headerObj[label];
2893             }
2894
2895             if (isDefault) {
2896                 this.hasDefaultHeaders = true;
2897             }
2898             else {
2899                 this.hasHeaders = true;
2900             }
2901         },
2902
2903
2904         setHeader:function(o)
2905         {
2906             if (this.hasDefaultHeaders) {
2907                 for (var prop in this.defaultHeaders) {
2908                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2909                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2910                     }
2911                 }
2912             }
2913
2914             if (this.hasHeaders) {
2915                 for (var prop in this.headers) {
2916                     if (this.headers.hasOwnProperty(prop)) {
2917                         o.conn.setRequestHeader(prop, this.headers[prop]);
2918                     }
2919                 }
2920                 this.headers = {};
2921                 this.hasHeaders = false;
2922             }
2923         },
2924
2925         resetDefaultHeaders:function() {
2926             delete this.defaultHeaders;
2927             this.defaultHeaders = {};
2928             this.hasDefaultHeaders = false;
2929         },
2930
2931         abort:function(o, callback, isTimeout)
2932         {
2933             if(this.isCallInProgress(o)) {
2934                 o.conn.abort();
2935                 window.clearInterval(this.poll[o.tId]);
2936                 delete this.poll[o.tId];
2937                 if (isTimeout) {
2938                     delete this.timeout[o.tId];
2939                 }
2940
2941                 this.handleTransactionResponse(o, callback, true);
2942
2943                 return true;
2944             }
2945             else {
2946                 return false;
2947             }
2948         },
2949
2950
2951         isCallInProgress:function(o)
2952         {
2953             if (o && o.conn) {
2954                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2955             }
2956             else {
2957
2958                 return false;
2959             }
2960         },
2961
2962
2963         releaseObject:function(o)
2964         {
2965
2966             o.conn = null;
2967
2968             o = null;
2969         },
2970
2971         activeX:[
2972         'MSXML2.XMLHTTP.3.0',
2973         'MSXML2.XMLHTTP',
2974         'Microsoft.XMLHTTP'
2975         ]
2976
2977
2978     };
2979 })();/*
2980  * Portions of this file are based on pieces of Yahoo User Interface Library
2981  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2982  * YUI licensed under the BSD License:
2983  * http://developer.yahoo.net/yui/license.txt
2984  * <script type="text/javascript">
2985  *
2986  */
2987
2988 Roo.lib.Region = function(t, r, b, l) {
2989     this.top = t;
2990     this[1] = t;
2991     this.right = r;
2992     this.bottom = b;
2993     this.left = l;
2994     this[0] = l;
2995 };
2996
2997
2998 Roo.lib.Region.prototype = {
2999     contains : function(region) {
3000         return ( region.left >= this.left &&
3001                  region.right <= this.right &&
3002                  region.top >= this.top &&
3003                  region.bottom <= this.bottom    );
3004
3005     },
3006
3007     getArea : function() {
3008         return ( (this.bottom - this.top) * (this.right - this.left) );
3009     },
3010
3011     intersect : function(region) {
3012         var t = Math.max(this.top, region.top);
3013         var r = Math.min(this.right, region.right);
3014         var b = Math.min(this.bottom, region.bottom);
3015         var l = Math.max(this.left, region.left);
3016
3017         if (b >= t && r >= l) {
3018             return new Roo.lib.Region(t, r, b, l);
3019         } else {
3020             return null;
3021         }
3022     },
3023     union : function(region) {
3024         var t = Math.min(this.top, region.top);
3025         var r = Math.max(this.right, region.right);
3026         var b = Math.max(this.bottom, region.bottom);
3027         var l = Math.min(this.left, region.left);
3028
3029         return new Roo.lib.Region(t, r, b, l);
3030     },
3031
3032     adjust : function(t, l, b, r) {
3033         this.top += t;
3034         this.left += l;
3035         this.right += r;
3036         this.bottom += b;
3037         return this;
3038     }
3039 };
3040
3041 Roo.lib.Region.getRegion = function(el) {
3042     var p = Roo.lib.Dom.getXY(el);
3043
3044     var t = p[1];
3045     var r = p[0] + el.offsetWidth;
3046     var b = p[1] + el.offsetHeight;
3047     var l = p[0];
3048
3049     return new Roo.lib.Region(t, r, b, l);
3050 };
3051 /*
3052  * Portions of this file are based on pieces of Yahoo User Interface Library
3053  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3054  * YUI licensed under the BSD License:
3055  * http://developer.yahoo.net/yui/license.txt
3056  * <script type="text/javascript">
3057  *
3058  */
3059 //@@dep Roo.lib.Region
3060
3061
3062 Roo.lib.Point = function(x, y) {
3063     if (x instanceof Array) {
3064         y = x[1];
3065         x = x[0];
3066     }
3067     this.x = this.right = this.left = this[0] = x;
3068     this.y = this.top = this.bottom = this[1] = y;
3069 };
3070
3071 Roo.lib.Point.prototype = new Roo.lib.Region();
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080  
3081 (function() {   
3082
3083     Roo.lib.Anim = {
3084         scroll : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3086         },
3087
3088         motion : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3090         },
3091
3092         color : function(el, args, duration, easing, cb, scope) {
3093             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3094         },
3095
3096         run : function(el, args, duration, easing, cb, scope, type) {
3097             type = type || Roo.lib.AnimBase;
3098             if (typeof easing == "string") {
3099                 easing = Roo.lib.Easing[easing];
3100             }
3101             var anim = new type(el, args, duration, easing);
3102             anim.animateX(function() {
3103                 Roo.callback(cb, scope);
3104             });
3105             return anim;
3106         }
3107     };
3108 })();/*
3109  * Portions of this file are based on pieces of Yahoo User Interface Library
3110  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3111  * YUI licensed under the BSD License:
3112  * http://developer.yahoo.net/yui/license.txt
3113  * <script type="text/javascript">
3114  *
3115  */
3116
3117 (function() {    
3118     var libFlyweight;
3119     
3120     function fly(el) {
3121         if (!libFlyweight) {
3122             libFlyweight = new Roo.Element.Flyweight();
3123         }
3124         libFlyweight.dom = el;
3125         return libFlyweight;
3126     }
3127
3128     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3129     
3130    
3131     
3132     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3133         if (el) {
3134             this.init(el, attributes, duration, method);
3135         }
3136     };
3137
3138     Roo.lib.AnimBase.fly = fly;
3139     
3140     
3141     
3142     Roo.lib.AnimBase.prototype = {
3143
3144         toString: function() {
3145             var el = this.getEl();
3146             var id = el.id || el.tagName;
3147             return ("Anim " + id);
3148         },
3149
3150         patterns: {
3151             noNegatives:        /width|height|opacity|padding/i,
3152             offsetAttribute:  /^((width|height)|(top|left))$/,
3153             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3154             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3155         },
3156
3157
3158         doMethod: function(attr, start, end) {
3159             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3160         },
3161
3162
3163         setAttribute: function(attr, val, unit) {
3164             if (this.patterns.noNegatives.test(attr)) {
3165                 val = (val > 0) ? val : 0;
3166             }
3167
3168             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3169         },
3170
3171
3172         getAttribute: function(attr) {
3173             var el = this.getEl();
3174             var val = fly(el).getStyle(attr);
3175
3176             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3177                 return parseFloat(val);
3178             }
3179
3180             var a = this.patterns.offsetAttribute.exec(attr) || [];
3181             var pos = !!( a[3] );
3182             var box = !!( a[2] );
3183
3184
3185             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3186                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3187             } else {
3188                 val = 0;
3189             }
3190
3191             return val;
3192         },
3193
3194
3195         getDefaultUnit: function(attr) {
3196             if (this.patterns.defaultUnit.test(attr)) {
3197                 return 'px';
3198             }
3199
3200             return '';
3201         },
3202
3203         animateX : function(callback, scope) {
3204             var f = function() {
3205                 this.onComplete.removeListener(f);
3206                 if (typeof callback == "function") {
3207                     callback.call(scope || this, this);
3208                 }
3209             };
3210             this.onComplete.addListener(f, this);
3211             this.animate();
3212         },
3213
3214
3215         setRuntimeAttribute: function(attr) {
3216             var start;
3217             var end;
3218             var attributes = this.attributes;
3219
3220             this.runtimeAttributes[attr] = {};
3221
3222             var isset = function(prop) {
3223                 return (typeof prop !== 'undefined');
3224             };
3225
3226             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3227                 return false;
3228             }
3229
3230             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3231
3232
3233             if (isset(attributes[attr]['to'])) {
3234                 end = attributes[attr]['to'];
3235             } else if (isset(attributes[attr]['by'])) {
3236                 if (start.constructor == Array) {
3237                     end = [];
3238                     for (var i = 0, len = start.length; i < len; ++i) {
3239                         end[i] = start[i] + attributes[attr]['by'][i];
3240                     }
3241                 } else {
3242                     end = start + attributes[attr]['by'];
3243                 }
3244             }
3245
3246             this.runtimeAttributes[attr].start = start;
3247             this.runtimeAttributes[attr].end = end;
3248
3249
3250             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3251         },
3252
3253
3254         init: function(el, attributes, duration, method) {
3255
3256             var isAnimated = false;
3257
3258
3259             var startTime = null;
3260
3261
3262             var actualFrames = 0;
3263
3264
3265             el = Roo.getDom(el);
3266
3267
3268             this.attributes = attributes || {};
3269
3270
3271             this.duration = duration || 1;
3272
3273
3274             this.method = method || Roo.lib.Easing.easeNone;
3275
3276
3277             this.useSeconds = true;
3278
3279
3280             this.currentFrame = 0;
3281
3282
3283             this.totalFrames = Roo.lib.AnimMgr.fps;
3284
3285
3286             this.getEl = function() {
3287                 return el;
3288             };
3289
3290
3291             this.isAnimated = function() {
3292                 return isAnimated;
3293             };
3294
3295
3296             this.getStartTime = function() {
3297                 return startTime;
3298             };
3299
3300             this.runtimeAttributes = {};
3301
3302
3303             this.animate = function() {
3304                 if (this.isAnimated()) {
3305                     return false;
3306                 }
3307
3308                 this.currentFrame = 0;
3309
3310                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3311
3312                 Roo.lib.AnimMgr.registerElement(this);
3313             };
3314
3315
3316             this.stop = function(finish) {
3317                 if (finish) {
3318                     this.currentFrame = this.totalFrames;
3319                     this._onTween.fire();
3320                 }
3321                 Roo.lib.AnimMgr.stop(this);
3322             };
3323
3324             var onStart = function() {
3325                 this.onStart.fire();
3326
3327                 this.runtimeAttributes = {};
3328                 for (var attr in this.attributes) {
3329                     this.setRuntimeAttribute(attr);
3330                 }
3331
3332                 isAnimated = true;
3333                 actualFrames = 0;
3334                 startTime = new Date();
3335             };
3336
3337
3338             var onTween = function() {
3339                 var data = {
3340                     duration: new Date() - this.getStartTime(),
3341                     currentFrame: this.currentFrame
3342                 };
3343
3344                 data.toString = function() {
3345                     return (
3346                             'duration: ' + data.duration +
3347                             ', currentFrame: ' + data.currentFrame
3348                             );
3349                 };
3350
3351                 this.onTween.fire(data);
3352
3353                 var runtimeAttributes = this.runtimeAttributes;
3354
3355                 for (var attr in runtimeAttributes) {
3356                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3357                 }
3358
3359                 actualFrames += 1;
3360             };
3361
3362             var onComplete = function() {
3363                 var actual_duration = (new Date() - startTime) / 1000 ;
3364
3365                 var data = {
3366                     duration: actual_duration,
3367                     frames: actualFrames,
3368                     fps: actualFrames / actual_duration
3369                 };
3370
3371                 data.toString = function() {
3372                     return (
3373                             'duration: ' + data.duration +
3374                             ', frames: ' + data.frames +
3375                             ', fps: ' + data.fps
3376                             );
3377                 };
3378
3379                 isAnimated = false;
3380                 actualFrames = 0;
3381                 this.onComplete.fire(data);
3382             };
3383
3384
3385             this._onStart = new Roo.util.Event(this);
3386             this.onStart = new Roo.util.Event(this);
3387             this.onTween = new Roo.util.Event(this);
3388             this._onTween = new Roo.util.Event(this);
3389             this.onComplete = new Roo.util.Event(this);
3390             this._onComplete = new Roo.util.Event(this);
3391             this._onStart.addListener(onStart);
3392             this._onTween.addListener(onTween);
3393             this._onComplete.addListener(onComplete);
3394         }
3395     };
3396 })();
3397 /*
3398  * Portions of this file are based on pieces of Yahoo User Interface Library
3399  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3400  * YUI licensed under the BSD License:
3401  * http://developer.yahoo.net/yui/license.txt
3402  * <script type="text/javascript">
3403  *
3404  */
3405
3406 Roo.lib.AnimMgr = new function() {
3407
3408     var thread = null;
3409
3410
3411     var queue = [];
3412
3413
3414     var tweenCount = 0;
3415
3416
3417     this.fps = 1000;
3418
3419
3420     this.delay = 1;
3421
3422
3423     this.registerElement = function(tween) {
3424         queue[queue.length] = tween;
3425         tweenCount += 1;
3426         tween._onStart.fire();
3427         this.start();
3428     };
3429
3430
3431     this.unRegister = function(tween, index) {
3432         tween._onComplete.fire();
3433         index = index || getIndex(tween);
3434         if (index != -1) {
3435             queue.splice(index, 1);
3436         }
3437
3438         tweenCount -= 1;
3439         if (tweenCount <= 0) {
3440             this.stop();
3441         }
3442     };
3443
3444
3445     this.start = function() {
3446         if (thread === null) {
3447             thread = setInterval(this.run, this.delay);
3448         }
3449     };
3450
3451
3452     this.stop = function(tween) {
3453         if (!tween) {
3454             clearInterval(thread);
3455
3456             for (var i = 0, len = queue.length; i < len; ++i) {
3457                 if (queue[0].isAnimated()) {
3458                     this.unRegister(queue[0], 0);
3459                 }
3460             }
3461
3462             queue = [];
3463             thread = null;
3464             tweenCount = 0;
3465         }
3466         else {
3467             this.unRegister(tween);
3468         }
3469     };
3470
3471
3472     this.run = function() {
3473         for (var i = 0, len = queue.length; i < len; ++i) {
3474             var tween = queue[i];
3475             if (!tween || !tween.isAnimated()) {
3476                 continue;
3477             }
3478
3479             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3480             {
3481                 tween.currentFrame += 1;
3482
3483                 if (tween.useSeconds) {
3484                     correctFrame(tween);
3485                 }
3486                 tween._onTween.fire();
3487             }
3488             else {
3489                 Roo.lib.AnimMgr.stop(tween, i);
3490             }
3491         }
3492     };
3493
3494     var getIndex = function(anim) {
3495         for (var i = 0, len = queue.length; i < len; ++i) {
3496             if (queue[i] == anim) {
3497                 return i;
3498             }
3499         }
3500         return -1;
3501     };
3502
3503
3504     var correctFrame = function(tween) {
3505         var frames = tween.totalFrames;
3506         var frame = tween.currentFrame;
3507         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3508         var elapsed = (new Date() - tween.getStartTime());
3509         var tweak = 0;
3510
3511         if (elapsed < tween.duration * 1000) {
3512             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3513         } else {
3514             tweak = frames - (frame + 1);
3515         }
3516         if (tweak > 0 && isFinite(tweak)) {
3517             if (tween.currentFrame + tweak >= frames) {
3518                 tweak = frames - (frame + 1);
3519             }
3520
3521             tween.currentFrame += tweak;
3522         }
3523     };
3524 };
3525
3526     /*
3527  * Portions of this file are based on pieces of Yahoo User Interface Library
3528  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3529  * YUI licensed under the BSD License:
3530  * http://developer.yahoo.net/yui/license.txt
3531  * <script type="text/javascript">
3532  *
3533  */
3534 Roo.lib.Bezier = new function() {
3535
3536         this.getPosition = function(points, t) {
3537             var n = points.length;
3538             var tmp = [];
3539
3540             for (var i = 0; i < n; ++i) {
3541                 tmp[i] = [points[i][0], points[i][1]];
3542             }
3543
3544             for (var j = 1; j < n; ++j) {
3545                 for (i = 0; i < n - j; ++i) {
3546                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3547                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3548                 }
3549             }
3550
3551             return [ tmp[0][0], tmp[0][1] ];
3552
3553         };
3554     };/*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 (function() {
3563
3564     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3565         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3566     };
3567
3568     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3569
3570     var fly = Roo.lib.AnimBase.fly;
3571     var Y = Roo.lib;
3572     var superclass = Y.ColorAnim.superclass;
3573     var proto = Y.ColorAnim.prototype;
3574
3575     proto.toString = function() {
3576         var el = this.getEl();
3577         var id = el.id || el.tagName;
3578         return ("ColorAnim " + id);
3579     };
3580
3581     proto.patterns.color = /color$/i;
3582     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3583     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3584     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3585     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3586
3587
3588     proto.parseColor = function(s) {
3589         if (s.length == 3) {
3590             return s;
3591         }
3592
3593         var c = this.patterns.hex.exec(s);
3594         if (c && c.length == 4) {
3595             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3596         }
3597
3598         c = this.patterns.rgb.exec(s);
3599         if (c && c.length == 4) {
3600             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3601         }
3602
3603         c = this.patterns.hex3.exec(s);
3604         if (c && c.length == 4) {
3605             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3606         }
3607
3608         return null;
3609     };
3610     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3611     proto.getAttribute = function(attr) {
3612         var el = this.getEl();
3613         if (this.patterns.color.test(attr)) {
3614             var val = fly(el).getStyle(attr);
3615
3616             if (this.patterns.transparent.test(val)) {
3617                 var parent = el.parentNode;
3618                 val = fly(parent).getStyle(attr);
3619
3620                 while (parent && this.patterns.transparent.test(val)) {
3621                     parent = parent.parentNode;
3622                     val = fly(parent).getStyle(attr);
3623                     if (parent.tagName.toUpperCase() == 'HTML') {
3624                         val = '#fff';
3625                     }
3626                 }
3627             }
3628         } else {
3629             val = superclass.getAttribute.call(this, attr);
3630         }
3631
3632         return val;
3633     };
3634     proto.getAttribute = function(attr) {
3635         var el = this.getEl();
3636         if (this.patterns.color.test(attr)) {
3637             var val = fly(el).getStyle(attr);
3638
3639             if (this.patterns.transparent.test(val)) {
3640                 var parent = el.parentNode;
3641                 val = fly(parent).getStyle(attr);
3642
3643                 while (parent && this.patterns.transparent.test(val)) {
3644                     parent = parent.parentNode;
3645                     val = fly(parent).getStyle(attr);
3646                     if (parent.tagName.toUpperCase() == 'HTML') {
3647                         val = '#fff';
3648                     }
3649                 }
3650             }
3651         } else {
3652             val = superclass.getAttribute.call(this, attr);
3653         }
3654
3655         return val;
3656     };
3657
3658     proto.doMethod = function(attr, start, end) {
3659         var val;
3660
3661         if (this.patterns.color.test(attr)) {
3662             val = [];
3663             for (var i = 0, len = start.length; i < len; ++i) {
3664                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3665             }
3666
3667             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3668         }
3669         else {
3670             val = superclass.doMethod.call(this, attr, start, end);
3671         }
3672
3673         return val;
3674     };
3675
3676     proto.setRuntimeAttribute = function(attr) {
3677         superclass.setRuntimeAttribute.call(this, attr);
3678
3679         if (this.patterns.color.test(attr)) {
3680             var attributes = this.attributes;
3681             var start = this.parseColor(this.runtimeAttributes[attr].start);
3682             var end = this.parseColor(this.runtimeAttributes[attr].end);
3683
3684             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3685                 end = this.parseColor(attributes[attr].by);
3686
3687                 for (var i = 0, len = start.length; i < len; ++i) {
3688                     end[i] = start[i] + end[i];
3689                 }
3690             }
3691
3692             this.runtimeAttributes[attr].start = start;
3693             this.runtimeAttributes[attr].end = end;
3694         }
3695     };
3696 })();
3697
3698 /*
3699  * Portions of this file are based on pieces of Yahoo User Interface Library
3700  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701  * YUI licensed under the BSD License:
3702  * http://developer.yahoo.net/yui/license.txt
3703  * <script type="text/javascript">
3704  *
3705  */
3706 Roo.lib.Easing = {
3707
3708
3709     easeNone: function (t, b, c, d) {
3710         return c * t / d + b;
3711     },
3712
3713
3714     easeIn: function (t, b, c, d) {
3715         return c * (t /= d) * t + b;
3716     },
3717
3718
3719     easeOut: function (t, b, c, d) {
3720         return -c * (t /= d) * (t - 2) + b;
3721     },
3722
3723
3724     easeBoth: function (t, b, c, d) {
3725         if ((t /= d / 2) < 1) {
3726             return c / 2 * t * t + b;
3727         }
3728
3729         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3730     },
3731
3732
3733     easeInStrong: function (t, b, c, d) {
3734         return c * (t /= d) * t * t * t + b;
3735     },
3736
3737
3738     easeOutStrong: function (t, b, c, d) {
3739         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3740     },
3741
3742
3743     easeBothStrong: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t * t * t + b;
3746         }
3747
3748         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3749     },
3750
3751
3752
3753     elasticIn: function (t, b, c, d, a, p) {
3754         if (t == 0) {
3755             return b;
3756         }
3757         if ((t /= d) == 1) {
3758             return b + c;
3759         }
3760         if (!p) {
3761             p = d * .3;
3762         }
3763
3764         if (!a || a < Math.abs(c)) {
3765             a = c;
3766             var s = p / 4;
3767         }
3768         else {
3769             var s = p / (2 * Math.PI) * Math.asin(c / a);
3770         }
3771
3772         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3773     },
3774
3775
3776     elasticOut: function (t, b, c, d, a, p) {
3777         if (t == 0) {
3778             return b;
3779         }
3780         if ((t /= d) == 1) {
3781             return b + c;
3782         }
3783         if (!p) {
3784             p = d * .3;
3785         }
3786
3787         if (!a || a < Math.abs(c)) {
3788             a = c;
3789             var s = p / 4;
3790         }
3791         else {
3792             var s = p / (2 * Math.PI) * Math.asin(c / a);
3793         }
3794
3795         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3796     },
3797
3798
3799     elasticBoth: function (t, b, c, d, a, p) {
3800         if (t == 0) {
3801             return b;
3802         }
3803
3804         if ((t /= d / 2) == 2) {
3805             return b + c;
3806         }
3807
3808         if (!p) {
3809             p = d * (.3 * 1.5);
3810         }
3811
3812         if (!a || a < Math.abs(c)) {
3813             a = c;
3814             var s = p / 4;
3815         }
3816         else {
3817             var s = p / (2 * Math.PI) * Math.asin(c / a);
3818         }
3819
3820         if (t < 1) {
3821             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3822                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3823         }
3824         return a * Math.pow(2, -10 * (t -= 1)) *
3825                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3826     },
3827
3828
3829
3830     backIn: function (t, b, c, d, s) {
3831         if (typeof s == 'undefined') {
3832             s = 1.70158;
3833         }
3834         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3835     },
3836
3837
3838     backOut: function (t, b, c, d, s) {
3839         if (typeof s == 'undefined') {
3840             s = 1.70158;
3841         }
3842         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3843     },
3844
3845
3846     backBoth: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850
3851         if ((t /= d / 2 ) < 1) {
3852             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3853         }
3854         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3855     },
3856
3857
3858     bounceIn: function (t, b, c, d) {
3859         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3860     },
3861
3862
3863     bounceOut: function (t, b, c, d) {
3864         if ((t /= d) < (1 / 2.75)) {
3865             return c * (7.5625 * t * t) + b;
3866         } else if (t < (2 / 2.75)) {
3867             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3868         } else if (t < (2.5 / 2.75)) {
3869             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3870         }
3871         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3872     },
3873
3874
3875     bounceBoth: function (t, b, c, d) {
3876         if (t < d / 2) {
3877             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3878         }
3879         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3880     }
3881 };/*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Motion = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Motion.superclass;
3901         var proto = Y.Motion.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Motion " + id);
3907         };
3908
3909         proto.patterns.points = /^points$/i;
3910
3911         proto.setAttribute = function(attr, val, unit) {
3912             if (this.patterns.points.test(attr)) {
3913                 unit = unit || 'px';
3914                 superclass.setAttribute.call(this, 'left', val[0], unit);
3915                 superclass.setAttribute.call(this, 'top', val[1], unit);
3916             } else {
3917                 superclass.setAttribute.call(this, attr, val, unit);
3918             }
3919         };
3920
3921         proto.getAttribute = function(attr) {
3922             if (this.patterns.points.test(attr)) {
3923                 var val = [
3924                         superclass.getAttribute.call(this, 'left'),
3925                         superclass.getAttribute.call(this, 'top')
3926                         ];
3927             } else {
3928                 val = superclass.getAttribute.call(this, attr);
3929             }
3930
3931             return val;
3932         };
3933
3934         proto.doMethod = function(attr, start, end) {
3935             var val = null;
3936
3937             if (this.patterns.points.test(attr)) {
3938                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3939                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3940             } else {
3941                 val = superclass.doMethod.call(this, attr, start, end);
3942             }
3943             return val;
3944         };
3945
3946         proto.setRuntimeAttribute = function(attr) {
3947             if (this.patterns.points.test(attr)) {
3948                 var el = this.getEl();
3949                 var attributes = this.attributes;
3950                 var start;
3951                 var control = attributes['points']['control'] || [];
3952                 var end;
3953                 var i, len;
3954
3955                 if (control.length > 0 && !(control[0] instanceof Array)) {
3956                     control = [control];
3957                 } else {
3958                     var tmp = [];
3959                     for (i = 0,len = control.length; i < len; ++i) {
3960                         tmp[i] = control[i];
3961                     }
3962                     control = tmp;
3963                 }
3964
3965                 Roo.fly(el).position();
3966
3967                 if (isset(attributes['points']['from'])) {
3968                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3969                 }
3970                 else {
3971                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3972                 }
3973
3974                 start = this.getAttribute('points');
3975
3976
3977                 if (isset(attributes['points']['to'])) {
3978                     end = translateValues.call(this, attributes['points']['to'], start);
3979
3980                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3981                     for (i = 0,len = control.length; i < len; ++i) {
3982                         control[i] = translateValues.call(this, control[i], start);
3983                     }
3984
3985
3986                 } else if (isset(attributes['points']['by'])) {
3987                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3988
3989                     for (i = 0,len = control.length; i < len; ++i) {
3990                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3991                     }
3992                 }
3993
3994                 this.runtimeAttributes[attr] = [start];
3995
3996                 if (control.length > 0) {
3997                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3998                 }
3999
4000                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4001             }
4002             else {
4003                 superclass.setRuntimeAttribute.call(this, attr);
4004             }
4005         };
4006
4007         var translateValues = function(val, start) {
4008             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4010
4011             return val;
4012         };
4013
4014         var isset = function(prop) {
4015             return (typeof prop !== 'undefined');
4016         };
4017     })();
4018 /*
4019  * Portions of this file are based on pieces of Yahoo User Interface Library
4020  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4021  * YUI licensed under the BSD License:
4022  * http://developer.yahoo.net/yui/license.txt
4023  * <script type="text/javascript">
4024  *
4025  */
4026     (function() {
4027         Roo.lib.Scroll = function(el, attributes, duration, method) {
4028             if (el) {
4029                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4030             }
4031         };
4032
4033         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4034
4035
4036         var Y = Roo.lib;
4037         var superclass = Y.Scroll.superclass;
4038         var proto = Y.Scroll.prototype;
4039
4040         proto.toString = function() {
4041             var el = this.getEl();
4042             var id = el.id || el.tagName;
4043             return ("Scroll " + id);
4044         };
4045
4046         proto.doMethod = function(attr, start, end) {
4047             var val = null;
4048
4049             if (attr == 'scroll') {
4050                 val = [
4051                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4052                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4053                         ];
4054
4055             } else {
4056                 val = superclass.doMethod.call(this, attr, start, end);
4057             }
4058             return val;
4059         };
4060
4061         proto.getAttribute = function(attr) {
4062             var val = null;
4063             var el = this.getEl();
4064
4065             if (attr == 'scroll') {
4066                 val = [ el.scrollLeft, el.scrollTop ];
4067             } else {
4068                 val = superclass.getAttribute.call(this, attr);
4069             }
4070
4071             return val;
4072         };
4073
4074         proto.setAttribute = function(attr, val, unit) {
4075             var el = this.getEl();
4076
4077             if (attr == 'scroll') {
4078                 el.scrollLeft = val[0];
4079                 el.scrollTop = val[1];
4080             } else {
4081                 superclass.setAttribute.call(this, attr, val, unit);
4082             }
4083         };
4084     })();
4085 /*
4086  * Based on:
4087  * Ext JS Library 1.1.1
4088  * Copyright(c) 2006-2007, Ext JS, LLC.
4089  *
4090  * Originally Released Under LGPL - original licence link has changed is not relivant.
4091  *
4092  * Fork - LGPL
4093  * <script type="text/javascript">
4094  */
4095
4096
4097 // nasty IE9 hack - what a pile of crap that is..
4098
4099  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4100     Range.prototype.createContextualFragment = function (html) {
4101         var doc = window.document;
4102         var container = doc.createElement("div");
4103         container.innerHTML = html;
4104         var frag = doc.createDocumentFragment(), n;
4105         while ((n = container.firstChild)) {
4106             frag.appendChild(n);
4107         }
4108         return frag;
4109     };
4110 }
4111
4112 /**
4113  * @class Roo.DomHelper
4114  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4115  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4116  * @singleton
4117  */
4118 Roo.DomHelper = function(){
4119     var tempTableEl = null;
4120     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4121     var tableRe = /^table|tbody|tr|td$/i;
4122     var xmlns = {};
4123     // build as innerHTML where available
4124     /** @ignore */
4125     var createHtml = function(o){
4126         if(typeof o == 'string'){
4127             return o;
4128         }
4129         var b = "";
4130         if(!o.tag){
4131             o.tag = "div";
4132         }
4133         b += "<" + o.tag;
4134         for(var attr in o){
4135             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4136             if(attr == "style"){
4137                 var s = o["style"];
4138                 if(typeof s == "function"){
4139                     s = s.call();
4140                 }
4141                 if(typeof s == "string"){
4142                     b += ' style="' + s + '"';
4143                 }else if(typeof s == "object"){
4144                     b += ' style="';
4145                     for(var key in s){
4146                         if(typeof s[key] != "function"){
4147                             b += key + ":" + s[key] + ";";
4148                         }
4149                     }
4150                     b += '"';
4151                 }
4152             }else{
4153                 if(attr == "cls"){
4154                     b += ' class="' + o["cls"] + '"';
4155                 }else if(attr == "htmlFor"){
4156                     b += ' for="' + o["htmlFor"] + '"';
4157                 }else{
4158                     b += " " + attr + '="' + o[attr] + '"';
4159                 }
4160             }
4161         }
4162         if(emptyTags.test(o.tag)){
4163             b += "/>";
4164         }else{
4165             b += ">";
4166             var cn = o.children || o.cn;
4167             if(cn){
4168                 //http://bugs.kde.org/show_bug.cgi?id=71506
4169                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4170                     for(var i = 0, len = cn.length; i < len; i++) {
4171                         b += createHtml(cn[i], b);
4172                     }
4173                 }else{
4174                     b += createHtml(cn, b);
4175                 }
4176             }
4177             if(o.html){
4178                 b += o.html;
4179             }
4180             b += "</" + o.tag + ">";
4181         }
4182         return b;
4183     };
4184
4185     // build as dom
4186     /** @ignore */
4187     var createDom = function(o, parentNode){
4188          
4189         // defininition craeted..
4190         var ns = false;
4191         if (o.ns && o.ns != 'html') {
4192                
4193             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4194                 xmlns[o.ns] = o.xmlns;
4195                 ns = o.xmlns;
4196             }
4197             if (typeof(xmlns[o.ns]) == 'undefined') {
4198                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4199             }
4200             ns = xmlns[o.ns];
4201         }
4202         
4203         
4204         if (typeof(o) == 'string') {
4205             return parentNode.appendChild(document.createTextNode(o));
4206         }
4207         o.tag = o.tag || div;
4208         if (o.ns && Roo.isIE) {
4209             ns = false;
4210             o.tag = o.ns + ':' + o.tag;
4211             
4212         }
4213         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4214         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4215         for(var attr in o){
4216             
4217             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4218                     attr == "style" || typeof o[attr] == "function") continue;
4219                     
4220             if(attr=="cls" && Roo.isIE){
4221                 el.className = o["cls"];
4222             }else{
4223                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4224                 else el[attr] = o[attr];
4225             }
4226         }
4227         Roo.DomHelper.applyStyles(el, o.style);
4228         var cn = o.children || o.cn;
4229         if(cn){
4230             //http://bugs.kde.org/show_bug.cgi?id=71506
4231              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4232                 for(var i = 0, len = cn.length; i < len; i++) {
4233                     createDom(cn[i], el);
4234                 }
4235             }else{
4236                 createDom(cn, el);
4237             }
4238         }
4239         if(o.html){
4240             el.innerHTML = o.html;
4241         }
4242         if(parentNode){
4243            parentNode.appendChild(el);
4244         }
4245         return el;
4246     };
4247
4248     var ieTable = function(depth, s, h, e){
4249         tempTableEl.innerHTML = [s, h, e].join('');
4250         var i = -1, el = tempTableEl;
4251         while(++i < depth){
4252             el = el.firstChild;
4253         }
4254         return el;
4255     };
4256
4257     // kill repeat to save bytes
4258     var ts = '<table>',
4259         te = '</table>',
4260         tbs = ts+'<tbody>',
4261         tbe = '</tbody>'+te,
4262         trs = tbs + '<tr>',
4263         tre = '</tr>'+tbe;
4264
4265     /**
4266      * @ignore
4267      * Nasty code for IE's broken table implementation
4268      */
4269     var insertIntoTable = function(tag, where, el, html){
4270         if(!tempTableEl){
4271             tempTableEl = document.createElement('div');
4272         }
4273         var node;
4274         var before = null;
4275         if(tag == 'td'){
4276             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4277                 return;
4278             }
4279             if(where == 'beforebegin'){
4280                 before = el;
4281                 el = el.parentNode;
4282             } else{
4283                 before = el.nextSibling;
4284                 el = el.parentNode;
4285             }
4286             node = ieTable(4, trs, html, tre);
4287         }
4288         else if(tag == 'tr'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(3, tbs, html, tbe);
4297             } else{ // INTO a TR
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(4, trs, html, tre);
4302             }
4303         } else if(tag == 'tbody'){
4304             if(where == 'beforebegin'){
4305                 before = el;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else if(where == 'afterend'){
4309                 before = el.nextSibling;
4310                 el = el.parentNode;
4311                 node = ieTable(2, ts, html, te);
4312             } else{
4313                 if(where == 'afterbegin'){
4314                     before = el.firstChild;
4315                 }
4316                 node = ieTable(3, tbs, html, tbe);
4317             }
4318         } else{ // TABLE
4319             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4320                 return;
4321             }
4322             if(where == 'afterbegin'){
4323                 before = el.firstChild;
4324             }
4325             node = ieTable(2, ts, html, te);
4326         }
4327         el.insertBefore(node, before);
4328         return node;
4329     };
4330
4331     return {
4332     /** True to force the use of DOM instead of html fragments @type Boolean */
4333     useDom : false,
4334
4335     /**
4336      * Returns the markup for the passed Element(s) config
4337      * @param {Object} o The Dom object spec (and children)
4338      * @return {String}
4339      */
4340     markup : function(o){
4341         return createHtml(o);
4342     },
4343
4344     /**
4345      * Applies a style specification to an element
4346      * @param {String/HTMLElement} el The element to apply styles to
4347      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4348      * a function which returns such a specification.
4349      */
4350     applyStyles : function(el, styles){
4351         if(styles){
4352            el = Roo.fly(el);
4353            if(typeof styles == "string"){
4354                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4355                var matches;
4356                while ((matches = re.exec(styles)) != null){
4357                    el.setStyle(matches[1], matches[2]);
4358                }
4359            }else if (typeof styles == "object"){
4360                for (var style in styles){
4361                   el.setStyle(style, styles[style]);
4362                }
4363            }else if (typeof styles == "function"){
4364                 Roo.DomHelper.applyStyles(el, styles.call());
4365            }
4366         }
4367     },
4368
4369     /**
4370      * Inserts an HTML fragment into the Dom
4371      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4372      * @param {HTMLElement} el The context element
4373      * @param {String} html The HTML fragmenet
4374      * @return {HTMLElement} The new node
4375      */
4376     insertHtml : function(where, el, html){
4377         where = where.toLowerCase();
4378         if(el.insertAdjacentHTML){
4379             if(tableRe.test(el.tagName)){
4380                 var rs;
4381                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4382                     return rs;
4383                 }
4384             }
4385             switch(where){
4386                 case "beforebegin":
4387                     el.insertAdjacentHTML('BeforeBegin', html);
4388                     return el.previousSibling;
4389                 case "afterbegin":
4390                     el.insertAdjacentHTML('AfterBegin', html);
4391                     return el.firstChild;
4392                 case "beforeend":
4393                     el.insertAdjacentHTML('BeforeEnd', html);
4394                     return el.lastChild;
4395                 case "afterend":
4396                     el.insertAdjacentHTML('AfterEnd', html);
4397                     return el.nextSibling;
4398             }
4399             throw 'Illegal insertion point -> "' + where + '"';
4400         }
4401         var range = el.ownerDocument.createRange();
4402         var frag;
4403         switch(where){
4404              case "beforebegin":
4405                 range.setStartBefore(el);
4406                 frag = range.createContextualFragment(html);
4407                 el.parentNode.insertBefore(frag, el);
4408                 return el.previousSibling;
4409              case "afterbegin":
4410                 if(el.firstChild){
4411                     range.setStartBefore(el.firstChild);
4412                     frag = range.createContextualFragment(html);
4413                     el.insertBefore(frag, el.firstChild);
4414                     return el.firstChild;
4415                 }else{
4416                     el.innerHTML = html;
4417                     return el.firstChild;
4418                 }
4419             case "beforeend":
4420                 if(el.lastChild){
4421                     range.setStartAfter(el.lastChild);
4422                     frag = range.createContextualFragment(html);
4423                     el.appendChild(frag);
4424                     return el.lastChild;
4425                 }else{
4426                     el.innerHTML = html;
4427                     return el.lastChild;
4428                 }
4429             case "afterend":
4430                 range.setStartAfter(el);
4431                 frag = range.createContextualFragment(html);
4432                 el.parentNode.insertBefore(frag, el.nextSibling);
4433                 return el.nextSibling;
4434             }
4435             throw 'Illegal insertion point -> "' + where + '"';
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and inserts them before el
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     insertBefore : function(el, o, returnElement){
4446         return this.doInsert(el, o, returnElement, "beforeBegin");
4447     },
4448
4449     /**
4450      * Creates new Dom element(s) and inserts them after el
4451      * @param {String/HTMLElement/Element} el The context element
4452      * @param {Object} o The Dom object spec (and children)
4453      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4454      * @return {HTMLElement/Roo.Element} The new node
4455      */
4456     insertAfter : function(el, o, returnElement){
4457         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4458     },
4459
4460     /**
4461      * Creates new Dom element(s) and inserts them as the first child of el
4462      * @param {String/HTMLElement/Element} el The context element
4463      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4464      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4465      * @return {HTMLElement/Roo.Element} The new node
4466      */
4467     insertFirst : function(el, o, returnElement){
4468         return this.doInsert(el, o, returnElement, "afterBegin");
4469     },
4470
4471     // private
4472     doInsert : function(el, o, returnElement, pos, sibling){
4473         el = Roo.getDom(el);
4474         var newNode;
4475         if(this.useDom || o.ns){
4476             newNode = createDom(o, null);
4477             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4478         }else{
4479             var html = createHtml(o);
4480             newNode = this.insertHtml(pos, el, html);
4481         }
4482         return returnElement ? Roo.get(newNode, true) : newNode;
4483     },
4484
4485     /**
4486      * Creates new Dom element(s) and appends them to el
4487      * @param {String/HTMLElement/Element} el The context element
4488      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4489      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4490      * @return {HTMLElement/Roo.Element} The new node
4491      */
4492     append : function(el, o, returnElement){
4493         el = Roo.getDom(el);
4494         var newNode;
4495         if(this.useDom || o.ns){
4496             newNode = createDom(o, null);
4497             el.appendChild(newNode);
4498         }else{
4499             var html = createHtml(o);
4500             newNode = this.insertHtml("beforeEnd", el, html);
4501         }
4502         return returnElement ? Roo.get(newNode, true) : newNode;
4503     },
4504
4505     /**
4506      * Creates new Dom element(s) and overwrites the contents of el with them
4507      * @param {String/HTMLElement/Element} el The context element
4508      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4510      * @return {HTMLElement/Roo.Element} The new node
4511      */
4512     overwrite : function(el, o, returnElement){
4513         el = Roo.getDom(el);
4514         if (o.ns) {
4515           
4516             while (el.childNodes.length) {
4517                 el.removeChild(el.firstChild);
4518             }
4519             createDom(o, el);
4520         } else {
4521             el.innerHTML = createHtml(o);   
4522         }
4523         
4524         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4525     },
4526
4527     /**
4528      * Creates a new Roo.DomHelper.Template from the Dom object spec
4529      * @param {Object} o The Dom object spec (and children)
4530      * @return {Roo.DomHelper.Template} The new template
4531      */
4532     createTemplate : function(o){
4533         var html = createHtml(o);
4534         return new Roo.Template(html);
4535     }
4536     };
4537 }();
4538 /*
4539  * Based on:
4540  * Ext JS Library 1.1.1
4541  * Copyright(c) 2006-2007, Ext JS, LLC.
4542  *
4543  * Originally Released Under LGPL - original licence link has changed is not relivant.
4544  *
4545  * Fork - LGPL
4546  * <script type="text/javascript">
4547  */
4548  
4549 /**
4550 * @class Roo.Template
4551 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4552 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4553 * Usage:
4554 <pre><code>
4555 var t = new Roo.Template({
4556     html :  '&lt;div name="{id}"&gt;' + 
4557         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4558         '&lt;/div&gt;',
4559     myformat: function (value, allValues) {
4560         return 'XX' + value;
4561     }
4562 });
4563 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4564 </code></pre>
4565 * For more information see this blog post with examples:
4566 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4567      - Create Elements using DOM, HTML fragments and Templates</a>. 
4568 * @constructor
4569 * @param {Object} cfg - Configuration object.
4570 */
4571 Roo.Template = function(cfg){
4572     // BC!
4573     if(cfg instanceof Array){
4574         cfg = cfg.join("");
4575     }else if(arguments.length > 1){
4576         cfg = Array.prototype.join.call(arguments, "");
4577     }
4578     
4579     
4580     if (typeof(cfg) == 'object') {
4581         Roo.apply(this,cfg)
4582     } else {
4583         // bc
4584         this.html = cfg;
4585     }
4586     if (this.url) {
4587         this.load();
4588     }
4589     
4590 };
4591 Roo.Template.prototype = {
4592     
4593     /**
4594      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4595      *                    it should be fixed so that template is observable...
4596      */
4597     url : false,
4598     /**
4599      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4600      */
4601     html : '',
4602     /**
4603      * Returns an HTML fragment of this template with the specified values applied.
4604      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4605      * @return {String} The HTML fragment
4606      */
4607     applyTemplate : function(values){
4608         try {
4609            
4610             if(this.compiled){
4611                 return this.compiled(values);
4612             }
4613             var useF = this.disableFormats !== true;
4614             var fm = Roo.util.Format, tpl = this;
4615             var fn = function(m, name, format, args){
4616                 if(format && useF){
4617                     if(format.substr(0, 5) == "this."){
4618                         return tpl.call(format.substr(5), values[name], values);
4619                     }else{
4620                         if(args){
4621                             // quoted values are required for strings in compiled templates, 
4622                             // but for non compiled we need to strip them
4623                             // quoted reversed for jsmin
4624                             var re = /^\s*['"](.*)["']\s*$/;
4625                             args = args.split(',');
4626                             for(var i = 0, len = args.length; i < len; i++){
4627                                 args[i] = args[i].replace(re, "$1");
4628                             }
4629                             args = [values[name]].concat(args);
4630                         }else{
4631                             args = [values[name]];
4632                         }
4633                         return fm[format].apply(fm, args);
4634                     }
4635                 }else{
4636                     return values[name] !== undefined ? values[name] : "";
4637                 }
4638             };
4639             return this.html.replace(this.re, fn);
4640         } catch (e) {
4641             Roo.log(e);
4642             throw e;
4643         }
4644          
4645     },
4646     
4647     loading : false,
4648       
4649     load : function ()
4650     {
4651          
4652         if (this.loading) {
4653             return;
4654         }
4655         var _t = this;
4656         
4657         this.loading = true;
4658         this.compiled = false;
4659         
4660         var cx = new Roo.data.Connection();
4661         cx.request({
4662             url : this.url,
4663             method : 'GET',
4664             success : function (response) {
4665                 _t.loading = false;
4666                 _t.html = response.responseText;
4667                 _t.url = false;
4668                 _t.compile();
4669              },
4670             failure : function(response) {
4671                 Roo.log("Template failed to load from " + _t.url);
4672                 _t.loading = false;
4673             }
4674         });
4675     },
4676
4677     /**
4678      * Sets the HTML used as the template and optionally compiles it.
4679      * @param {String} html
4680      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4681      * @return {Roo.Template} this
4682      */
4683     set : function(html, compile){
4684         this.html = html;
4685         this.compiled = null;
4686         if(compile){
4687             this.compile();
4688         }
4689         return this;
4690     },
4691     
4692     /**
4693      * True to disable format functions (defaults to false)
4694      * @type Boolean
4695      */
4696     disableFormats : false,
4697     
4698     /**
4699     * The regular expression used to match template variables 
4700     * @type RegExp
4701     * @property 
4702     */
4703     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4704     
4705     /**
4706      * Compiles the template into an internal function, eliminating the RegEx overhead.
4707      * @return {Roo.Template} this
4708      */
4709     compile : function(){
4710         var fm = Roo.util.Format;
4711         var useF = this.disableFormats !== true;
4712         var sep = Roo.isGecko ? "+" : ",";
4713         var fn = function(m, name, format, args){
4714             if(format && useF){
4715                 args = args ? ',' + args : "";
4716                 if(format.substr(0, 5) != "this."){
4717                     format = "fm." + format + '(';
4718                 }else{
4719                     format = 'this.call("'+ format.substr(5) + '", ';
4720                     args = ", values";
4721                 }
4722             }else{
4723                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4724             }
4725             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4726         };
4727         var body;
4728         // branched to use + in gecko and [].join() in others
4729         if(Roo.isGecko){
4730             body = "this.compiled = function(values){ return '" +
4731                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4732                     "';};";
4733         }else{
4734             body = ["this.compiled = function(values){ return ['"];
4735             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4736             body.push("'].join('');};");
4737             body = body.join('');
4738         }
4739         /**
4740          * eval:var:values
4741          * eval:var:fm
4742          */
4743         eval(body);
4744         return this;
4745     },
4746     
4747     // private function used to call members
4748     call : function(fnName, value, allValues){
4749         return this[fnName](value, allValues);
4750     },
4751     
4752     /**
4753      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4754      * @param {String/HTMLElement/Roo.Element} el The context element
4755      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4756      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4757      * @return {HTMLElement/Roo.Element} The new node or Element
4758      */
4759     insertFirst: function(el, values, returnElement){
4760         return this.doInsert('afterBegin', el, values, returnElement);
4761     },
4762
4763     /**
4764      * Applies the supplied values to the template and inserts the new node(s) before el.
4765      * @param {String/HTMLElement/Roo.Element} el The context element
4766      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4767      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4768      * @return {HTMLElement/Roo.Element} The new node or Element
4769      */
4770     insertBefore: function(el, values, returnElement){
4771         return this.doInsert('beforeBegin', el, values, returnElement);
4772     },
4773
4774     /**
4775      * Applies the supplied values to the template and inserts the new node(s) after el.
4776      * @param {String/HTMLElement/Roo.Element} el The context element
4777      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4778      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4779      * @return {HTMLElement/Roo.Element} The new node or Element
4780      */
4781     insertAfter : function(el, values, returnElement){
4782         return this.doInsert('afterEnd', el, values, returnElement);
4783     },
4784     
4785     /**
4786      * Applies the supplied values to the template and appends the new node(s) to el.
4787      * @param {String/HTMLElement/Roo.Element} el The context element
4788      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4789      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4790      * @return {HTMLElement/Roo.Element} The new node or Element
4791      */
4792     append : function(el, values, returnElement){
4793         return this.doInsert('beforeEnd', el, values, returnElement);
4794     },
4795
4796     doInsert : function(where, el, values, returnEl){
4797         el = Roo.getDom(el);
4798         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4799         return returnEl ? Roo.get(newNode, true) : newNode;
4800     },
4801
4802     /**
4803      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4804      * @param {String/HTMLElement/Roo.Element} el The context element
4805      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4806      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4807      * @return {HTMLElement/Roo.Element} The new node or Element
4808      */
4809     overwrite : function(el, values, returnElement){
4810         el = Roo.getDom(el);
4811         el.innerHTML = this.applyTemplate(values);
4812         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4813     }
4814 };
4815 /**
4816  * Alias for {@link #applyTemplate}
4817  * @method
4818  */
4819 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4820
4821 // backwards compat
4822 Roo.DomHelper.Template = Roo.Template;
4823
4824 /**
4825  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4826  * @param {String/HTMLElement} el A DOM element or its id
4827  * @returns {Roo.Template} The created template
4828  * @static
4829  */
4830 Roo.Template.from = function(el){
4831     el = Roo.getDom(el);
4832     return new Roo.Template(el.value || el.innerHTML);
4833 };/*
4834  * Based on:
4835  * Ext JS Library 1.1.1
4836  * Copyright(c) 2006-2007, Ext JS, LLC.
4837  *
4838  * Originally Released Under LGPL - original licence link has changed is not relivant.
4839  *
4840  * Fork - LGPL
4841  * <script type="text/javascript">
4842  */
4843  
4844
4845 /*
4846  * This is code is also distributed under MIT license for use
4847  * with jQuery and prototype JavaScript libraries.
4848  */
4849 /**
4850  * @class Roo.DomQuery
4851 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4852 <p>
4853 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4854
4855 <p>
4856 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4857 </p>
4858 <h4>Element Selectors:</h4>
4859 <ul class="list">
4860     <li> <b>*</b> any element</li>
4861     <li> <b>E</b> an element with the tag E</li>
4862     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4863     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4864     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4865     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4866 </ul>
4867 <h4>Attribute Selectors:</h4>
4868 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4869 <ul class="list">
4870     <li> <b>E[foo]</b> has an attribute "foo"</li>
4871     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4872     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4873     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4874     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4875     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4876     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4877 </ul>
4878 <h4>Pseudo Classes:</h4>
4879 <ul class="list">
4880     <li> <b>E:first-child</b> E is the first child of its parent</li>
4881     <li> <b>E:last-child</b> E is the last child of its parent</li>
4882     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4883     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4884     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4885     <li> <b>E:only-child</b> E is the only child of its parent</li>
4886     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4887     <li> <b>E:first</b> the first E in the resultset</li>
4888     <li> <b>E:last</b> the last E in the resultset</li>
4889     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4890     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4891     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4892     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4893     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4894     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4895     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4896     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4897     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4898 </ul>
4899 <h4>CSS Value Selectors:</h4>
4900 <ul class="list">
4901     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4902     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4903     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4904     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4905     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4906     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4907 </ul>
4908  * @singleton
4909  */
4910 Roo.DomQuery = function(){
4911     var cache = {}, simpleCache = {}, valueCache = {};
4912     var nonSpace = /\S/;
4913     var trimRe = /^\s+|\s+$/g;
4914     var tplRe = /\{(\d+)\}/g;
4915     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4916     var tagTokenRe = /^(#)?([\w-\*]+)/;
4917     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4918
4919     function child(p, index){
4920         var i = 0;
4921         var n = p.firstChild;
4922         while(n){
4923             if(n.nodeType == 1){
4924                if(++i == index){
4925                    return n;
4926                }
4927             }
4928             n = n.nextSibling;
4929         }
4930         return null;
4931     };
4932
4933     function next(n){
4934         while((n = n.nextSibling) && n.nodeType != 1);
4935         return n;
4936     };
4937
4938     function prev(n){
4939         while((n = n.previousSibling) && n.nodeType != 1);
4940         return n;
4941     };
4942
4943     function children(d){
4944         var n = d.firstChild, ni = -1;
4945             while(n){
4946                 var nx = n.nextSibling;
4947                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4948                     d.removeChild(n);
4949                 }else{
4950                     n.nodeIndex = ++ni;
4951                 }
4952                 n = nx;
4953             }
4954             return this;
4955         };
4956
4957     function byClassName(c, a, v){
4958         if(!v){
4959             return c;
4960         }
4961         var r = [], ri = -1, cn;
4962         for(var i = 0, ci; ci = c[i]; i++){
4963             if((' '+ci.className+' ').indexOf(v) != -1){
4964                 r[++ri] = ci;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function attrValue(n, attr){
4971         if(!n.tagName && typeof n.length != "undefined"){
4972             n = n[0];
4973         }
4974         if(!n){
4975             return null;
4976         }
4977         if(attr == "for"){
4978             return n.htmlFor;
4979         }
4980         if(attr == "class" || attr == "className"){
4981             return n.className;
4982         }
4983         return n.getAttribute(attr) || n[attr];
4984
4985     };
4986
4987     function getNodes(ns, mode, tagName){
4988         var result = [], ri = -1, cs;
4989         if(!ns){
4990             return result;
4991         }
4992         tagName = tagName || "*";
4993         if(typeof ns.getElementsByTagName != "undefined"){
4994             ns = [ns];
4995         }
4996         if(!mode){
4997             for(var i = 0, ni; ni = ns[i]; i++){
4998                 cs = ni.getElementsByTagName(tagName);
4999                 for(var j = 0, ci; ci = cs[j]; j++){
5000                     result[++ri] = ci;
5001                 }
5002             }
5003         }else if(mode == "/" || mode == ">"){
5004             var utag = tagName.toUpperCase();
5005             for(var i = 0, ni, cn; ni = ns[i]; i++){
5006                 cn = ni.children || ni.childNodes;
5007                 for(var j = 0, cj; cj = cn[j]; j++){
5008                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5009                         result[++ri] = cj;
5010                     }
5011                 }
5012             }
5013         }else if(mode == "+"){
5014             var utag = tagName.toUpperCase();
5015             for(var i = 0, n; n = ns[i]; i++){
5016                 while((n = n.nextSibling) && n.nodeType != 1);
5017                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5018                     result[++ri] = n;
5019                 }
5020             }
5021         }else if(mode == "~"){
5022             for(var i = 0, n; n = ns[i]; i++){
5023                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5024                 if(n){
5025                     result[++ri] = n;
5026                 }
5027             }
5028         }
5029         return result;
5030     };
5031
5032     function concat(a, b){
5033         if(b.slice){
5034             return a.concat(b);
5035         }
5036         for(var i = 0, l = b.length; i < l; i++){
5037             a[a.length] = b[i];
5038         }
5039         return a;
5040     }
5041
5042     function byTag(cs, tagName){
5043         if(cs.tagName || cs == document){
5044             cs = [cs];
5045         }
5046         if(!tagName){
5047             return cs;
5048         }
5049         var r = [], ri = -1;
5050         tagName = tagName.toLowerCase();
5051         for(var i = 0, ci; ci = cs[i]; i++){
5052             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5053                 r[++ri] = ci;
5054             }
5055         }
5056         return r;
5057     };
5058
5059     function byId(cs, attr, id){
5060         if(cs.tagName || cs == document){
5061             cs = [cs];
5062         }
5063         if(!id){
5064             return cs;
5065         }
5066         var r = [], ri = -1;
5067         for(var i = 0,ci; ci = cs[i]; i++){
5068             if(ci && ci.id == id){
5069                 r[++ri] = ci;
5070                 return r;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function byAttribute(cs, attr, value, op, custom){
5077         var r = [], ri = -1, st = custom=="{";
5078         var f = Roo.DomQuery.operators[op];
5079         for(var i = 0, ci; ci = cs[i]; i++){
5080             var a;
5081             if(st){
5082                 a = Roo.DomQuery.getStyle(ci, attr);
5083             }
5084             else if(attr == "class" || attr == "className"){
5085                 a = ci.className;
5086             }else if(attr == "for"){
5087                 a = ci.htmlFor;
5088             }else if(attr == "href"){
5089                 a = ci.getAttribute("href", 2);
5090             }else{
5091                 a = ci.getAttribute(attr);
5092             }
5093             if((f && f(a, value)) || (!f && a)){
5094                 r[++ri] = ci;
5095             }
5096         }
5097         return r;
5098     };
5099
5100     function byPseudo(cs, name, value){
5101         return Roo.DomQuery.pseudos[name](cs, value);
5102     };
5103
5104     // This is for IE MSXML which does not support expandos.
5105     // IE runs the same speed using setAttribute, however FF slows way down
5106     // and Safari completely fails so they need to continue to use expandos.
5107     var isIE = window.ActiveXObject ? true : false;
5108
5109     // this eval is stop the compressor from
5110     // renaming the variable to something shorter
5111     
5112     /** eval:var:batch */
5113     var batch = 30803; 
5114
5115     var key = 30803;
5116
5117     function nodupIEXml(cs){
5118         var d = ++key;
5119         cs[0].setAttribute("_nodup", d);
5120         var r = [cs[0]];
5121         for(var i = 1, len = cs.length; i < len; i++){
5122             var c = cs[i];
5123             if(!c.getAttribute("_nodup") != d){
5124                 c.setAttribute("_nodup", d);
5125                 r[r.length] = c;
5126             }
5127         }
5128         for(var i = 0, len = cs.length; i < len; i++){
5129             cs[i].removeAttribute("_nodup");
5130         }
5131         return r;
5132     }
5133
5134     function nodup(cs){
5135         if(!cs){
5136             return [];
5137         }
5138         var len = cs.length, c, i, r = cs, cj, ri = -1;
5139         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5140             return cs;
5141         }
5142         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5143             return nodupIEXml(cs);
5144         }
5145         var d = ++key;
5146         cs[0]._nodup = d;
5147         for(i = 1; c = cs[i]; i++){
5148             if(c._nodup != d){
5149                 c._nodup = d;
5150             }else{
5151                 r = [];
5152                 for(var j = 0; j < i; j++){
5153                     r[++ri] = cs[j];
5154                 }
5155                 for(j = i+1; cj = cs[j]; j++){
5156                     if(cj._nodup != d){
5157                         cj._nodup = d;
5158                         r[++ri] = cj;
5159                     }
5160                 }
5161                 return r;
5162             }
5163         }
5164         return r;
5165     }
5166
5167     function quickDiffIEXml(c1, c2){
5168         var d = ++key;
5169         for(var i = 0, len = c1.length; i < len; i++){
5170             c1[i].setAttribute("_qdiff", d);
5171         }
5172         var r = [];
5173         for(var i = 0, len = c2.length; i < len; i++){
5174             if(c2[i].getAttribute("_qdiff") != d){
5175                 r[r.length] = c2[i];
5176             }
5177         }
5178         for(var i = 0, len = c1.length; i < len; i++){
5179            c1[i].removeAttribute("_qdiff");
5180         }
5181         return r;
5182     }
5183
5184     function quickDiff(c1, c2){
5185         var len1 = c1.length;
5186         if(!len1){
5187             return c2;
5188         }
5189         if(isIE && c1[0].selectSingleNode){
5190             return quickDiffIEXml(c1, c2);
5191         }
5192         var d = ++key;
5193         for(var i = 0; i < len1; i++){
5194             c1[i]._qdiff = d;
5195         }
5196         var r = [];
5197         for(var i = 0, len = c2.length; i < len; i++){
5198             if(c2[i]._qdiff != d){
5199                 r[r.length] = c2[i];
5200             }
5201         }
5202         return r;
5203     }
5204
5205     function quickId(ns, mode, root, id){
5206         if(ns == root){
5207            var d = root.ownerDocument || root;
5208            return d.getElementById(id);
5209         }
5210         ns = getNodes(ns, mode, "*");
5211         return byId(ns, null, id);
5212     }
5213
5214     return {
5215         getStyle : function(el, name){
5216             return Roo.fly(el).getStyle(name);
5217         },
5218         /**
5219          * Compiles a selector/xpath query into a reusable function. The returned function
5220          * takes one parameter "root" (optional), which is the context node from where the query should start.
5221          * @param {String} selector The selector/xpath query
5222          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5223          * @return {Function}
5224          */
5225         compile : function(path, type){
5226             type = type || "select";
5227             
5228             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5229             var q = path, mode, lq;
5230             var tk = Roo.DomQuery.matchers;
5231             var tklen = tk.length;
5232             var mm;
5233
5234             // accept leading mode switch
5235             var lmode = q.match(modeRe);
5236             if(lmode && lmode[1]){
5237                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5238                 q = q.replace(lmode[1], "");
5239             }
5240             // strip leading slashes
5241             while(path.substr(0, 1)=="/"){
5242                 path = path.substr(1);
5243             }
5244
5245             while(q && lq != q){
5246                 lq = q;
5247                 var tm = q.match(tagTokenRe);
5248                 if(type == "select"){
5249                     if(tm){
5250                         if(tm[1] == "#"){
5251                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5252                         }else{
5253                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5254                         }
5255                         q = q.replace(tm[0], "");
5256                     }else if(q.substr(0, 1) != '@'){
5257                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5258                     }
5259                 }else{
5260                     if(tm){
5261                         if(tm[1] == "#"){
5262                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5263                         }else{
5264                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5265                         }
5266                         q = q.replace(tm[0], "");
5267                     }
5268                 }
5269                 while(!(mm = q.match(modeRe))){
5270                     var matched = false;
5271                     for(var j = 0; j < tklen; j++){
5272                         var t = tk[j];
5273                         var m = q.match(t.re);
5274                         if(m){
5275                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5276                                                     return m[i];
5277                                                 });
5278                             q = q.replace(m[0], "");
5279                             matched = true;
5280                             break;
5281                         }
5282                     }
5283                     // prevent infinite loop on bad selector
5284                     if(!matched){
5285                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5286                     }
5287                 }
5288                 if(mm[1]){
5289                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5290                     q = q.replace(mm[1], "");
5291                 }
5292             }
5293             fn[fn.length] = "return nodup(n);\n}";
5294             
5295              /** 
5296               * list of variables that need from compression as they are used by eval.
5297              *  eval:var:batch 
5298              *  eval:var:nodup
5299              *  eval:var:byTag
5300              *  eval:var:ById
5301              *  eval:var:getNodes
5302              *  eval:var:quickId
5303              *  eval:var:mode
5304              *  eval:var:root
5305              *  eval:var:n
5306              *  eval:var:byClassName
5307              *  eval:var:byPseudo
5308              *  eval:var:byAttribute
5309              *  eval:var:attrValue
5310              * 
5311              **/ 
5312             eval(fn.join(""));
5313             return f;
5314         },
5315
5316         /**
5317          * Selects a group of elements.
5318          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5319          * @param {Node} root (optional) The start of the query (defaults to document).
5320          * @return {Array}
5321          */
5322         select : function(path, root, type){
5323             if(!root || root == document){
5324                 root = document;
5325             }
5326             if(typeof root == "string"){
5327                 root = document.getElementById(root);
5328             }
5329             var paths = path.split(",");
5330             var results = [];
5331             for(var i = 0, len = paths.length; i < len; i++){
5332                 var p = paths[i].replace(trimRe, "");
5333                 if(!cache[p]){
5334                     cache[p] = Roo.DomQuery.compile(p);
5335                     if(!cache[p]){
5336                         throw p + " is not a valid selector";
5337                     }
5338                 }
5339                 var result = cache[p](root);
5340                 if(result && result != document){
5341                     results = results.concat(result);
5342                 }
5343             }
5344             if(paths.length > 1){
5345                 return nodup(results);
5346             }
5347             return results;
5348         },
5349
5350         /**
5351          * Selects a single element.
5352          * @param {String} selector The selector/xpath query
5353          * @param {Node} root (optional) The start of the query (defaults to document).
5354          * @return {Element}
5355          */
5356         selectNode : function(path, root){
5357             return Roo.DomQuery.select(path, root)[0];
5358         },
5359
5360         /**
5361          * Selects the value of a node, optionally replacing null with the defaultValue.
5362          * @param {String} selector The selector/xpath query
5363          * @param {Node} root (optional) The start of the query (defaults to document).
5364          * @param {String} defaultValue
5365          */
5366         selectValue : function(path, root, defaultValue){
5367             path = path.replace(trimRe, "");
5368             if(!valueCache[path]){
5369                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5370             }
5371             var n = valueCache[path](root);
5372             n = n[0] ? n[0] : n;
5373             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5374             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5375         },
5376
5377         /**
5378          * Selects the value of a node, parsing integers and floats.
5379          * @param {String} selector The selector/xpath query
5380          * @param {Node} root (optional) The start of the query (defaults to document).
5381          * @param {Number} defaultValue
5382          * @return {Number}
5383          */
5384         selectNumber : function(path, root, defaultValue){
5385             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5386             return parseFloat(v);
5387         },
5388
5389         /**
5390          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5391          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5392          * @param {String} selector The simple selector to test
5393          * @return {Boolean}
5394          */
5395         is : function(el, ss){
5396             if(typeof el == "string"){
5397                 el = document.getElementById(el);
5398             }
5399             var isArray = (el instanceof Array);
5400             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5401             return isArray ? (result.length == el.length) : (result.length > 0);
5402         },
5403
5404         /**
5405          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5406          * @param {Array} el An array of elements to filter
5407          * @param {String} selector The simple selector to test
5408          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5409          * the selector instead of the ones that match
5410          * @return {Array}
5411          */
5412         filter : function(els, ss, nonMatches){
5413             ss = ss.replace(trimRe, "");
5414             if(!simpleCache[ss]){
5415                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5416             }
5417             var result = simpleCache[ss](els);
5418             return nonMatches ? quickDiff(result, els) : result;
5419         },
5420
5421         /**
5422          * Collection of matching regular expressions and code snippets.
5423          */
5424         matchers : [{
5425                 re: /^\.([\w-]+)/,
5426                 select: 'n = byClassName(n, null, " {1} ");'
5427             }, {
5428                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5429                 select: 'n = byPseudo(n, "{1}", "{2}");'
5430             },{
5431                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5432                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5433             }, {
5434                 re: /^#([\w-]+)/,
5435                 select: 'n = byId(n, null, "{1}");'
5436             },{
5437                 re: /^@([\w-]+)/,
5438                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5439             }
5440         ],
5441
5442         /**
5443          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5444          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5445          */
5446         operators : {
5447             "=" : function(a, v){
5448                 return a == v;
5449             },
5450             "!=" : function(a, v){
5451                 return a != v;
5452             },
5453             "^=" : function(a, v){
5454                 return a && a.substr(0, v.length) == v;
5455             },
5456             "$=" : function(a, v){
5457                 return a && a.substr(a.length-v.length) == v;
5458             },
5459             "*=" : function(a, v){
5460                 return a && a.indexOf(v) !== -1;
5461             },
5462             "%=" : function(a, v){
5463                 return (a % v) == 0;
5464             },
5465             "|=" : function(a, v){
5466                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5467             },
5468             "~=" : function(a, v){
5469                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5470             }
5471         },
5472
5473         /**
5474          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5475          * and the argument (if any) supplied in the selector.
5476          */
5477         pseudos : {
5478             "first-child" : function(c){
5479                 var r = [], ri = -1, n;
5480                 for(var i = 0, ci; ci = n = c[i]; i++){
5481                     while((n = n.previousSibling) && n.nodeType != 1);
5482                     if(!n){
5483                         r[++ri] = ci;
5484                     }
5485                 }
5486                 return r;
5487             },
5488
5489             "last-child" : function(c){
5490                 var r = [], ri = -1, n;
5491                 for(var i = 0, ci; ci = n = c[i]; i++){
5492                     while((n = n.nextSibling) && n.nodeType != 1);
5493                     if(!n){
5494                         r[++ri] = ci;
5495                     }
5496                 }
5497                 return r;
5498             },
5499
5500             "nth-child" : function(c, a) {
5501                 var r = [], ri = -1;
5502                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5503                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5504                 for(var i = 0, n; n = c[i]; i++){
5505                     var pn = n.parentNode;
5506                     if (batch != pn._batch) {
5507                         var j = 0;
5508                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5509                             if(cn.nodeType == 1){
5510                                cn.nodeIndex = ++j;
5511                             }
5512                         }
5513                         pn._batch = batch;
5514                     }
5515                     if (f == 1) {
5516                         if (l == 0 || n.nodeIndex == l){
5517                             r[++ri] = n;
5518                         }
5519                     } else if ((n.nodeIndex + l) % f == 0){
5520                         r[++ri] = n;
5521                     }
5522                 }
5523
5524                 return r;
5525             },
5526
5527             "only-child" : function(c){
5528                 var r = [], ri = -1;;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     if(!prev(ci) && !next(ci)){
5531                         r[++ri] = ci;
5532                     }
5533                 }
5534                 return r;
5535             },
5536
5537             "empty" : function(c){
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     var cns = ci.childNodes, j = 0, cn, empty = true;
5541                     while(cn = cns[j]){
5542                         ++j;
5543                         if(cn.nodeType == 1 || cn.nodeType == 3){
5544                             empty = false;
5545                             break;
5546                         }
5547                     }
5548                     if(empty){
5549                         r[++ri] = ci;
5550                     }
5551                 }
5552                 return r;
5553             },
5554
5555             "contains" : function(c, v){
5556                 var r = [], ri = -1;
5557                 for(var i = 0, ci; ci = c[i]; i++){
5558                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             },
5564
5565             "nodeValue" : function(c, v){
5566                 var r = [], ri = -1;
5567                 for(var i = 0, ci; ci = c[i]; i++){
5568                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5569                         r[++ri] = ci;
5570                     }
5571                 }
5572                 return r;
5573             },
5574
5575             "checked" : function(c){
5576                 var r = [], ri = -1;
5577                 for(var i = 0, ci; ci = c[i]; i++){
5578                     if(ci.checked == true){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "not" : function(c, ss){
5586                 return Roo.DomQuery.filter(c, ss, true);
5587             },
5588
5589             "odd" : function(c){
5590                 return this["nth-child"](c, "odd");
5591             },
5592
5593             "even" : function(c){
5594                 return this["nth-child"](c, "even");
5595             },
5596
5597             "nth" : function(c, a){
5598                 return c[a-1] || [];
5599             },
5600
5601             "first" : function(c){
5602                 return c[0] || [];
5603             },
5604
5605             "last" : function(c){
5606                 return c[c.length-1] || [];
5607             },
5608
5609             "has" : function(c, ss){
5610                 var s = Roo.DomQuery.select;
5611                 var r = [], ri = -1;
5612                 for(var i = 0, ci; ci = c[i]; i++){
5613                     if(s(ss, ci).length > 0){
5614                         r[++ri] = ci;
5615                     }
5616                 }
5617                 return r;
5618             },
5619
5620             "next" : function(c, ss){
5621                 var is = Roo.DomQuery.is;
5622                 var r = [], ri = -1;
5623                 for(var i = 0, ci; ci = c[i]; i++){
5624                     var n = next(ci);
5625                     if(n && is(n, ss)){
5626                         r[++ri] = ci;
5627                     }
5628                 }
5629                 return r;
5630             },
5631
5632             "prev" : function(c, ss){
5633                 var is = Roo.DomQuery.is;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     var n = prev(ci);
5637                     if(n && is(n, ss)){
5638                         r[++ri] = ci;
5639                     }
5640                 }
5641                 return r;
5642             }
5643         }
5644     };
5645 }();
5646
5647 /**
5648  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5649  * @param {String} path The selector/xpath query
5650  * @param {Node} root (optional) The start of the query (defaults to document).
5651  * @return {Array}
5652  * @member Roo
5653  * @method query
5654  */
5655 Roo.query = Roo.DomQuery.select;
5656 /*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.util.Observable
5669  * Base class that provides a common interface for publishing events. Subclasses are expected to
5670  * to have a property "events" with all the events defined.<br>
5671  * For example:
5672  * <pre><code>
5673  Employee = function(name){
5674     this.name = name;
5675     this.addEvents({
5676         "fired" : true,
5677         "quit" : true
5678     });
5679  }
5680  Roo.extend(Employee, Roo.util.Observable);
5681 </code></pre>
5682  * @param {Object} config properties to use (incuding events / listeners)
5683  */
5684
5685 Roo.util.Observable = function(cfg){
5686     
5687     cfg = cfg|| {};
5688     this.addEvents(cfg.events || {});
5689     if (cfg.events) {
5690         delete cfg.events; // make sure
5691     }
5692      
5693     Roo.apply(this, cfg);
5694     
5695     if(this.listeners){
5696         this.on(this.listeners);
5697         delete this.listeners;
5698     }
5699 };
5700 Roo.util.Observable.prototype = {
5701     /** 
5702  * @cfg {Object} listeners  list of events and functions to call for this object, 
5703  * For example :
5704  * <pre><code>
5705     listeners :  { 
5706        'click' : function(e) {
5707            ..... 
5708         } ,
5709         .... 
5710     } 
5711   </code></pre>
5712  */
5713     
5714     
5715     /**
5716      * Fires the specified event with the passed parameters (minus the event name).
5717      * @param {String} eventName
5718      * @param {Object...} args Variable number of parameters are passed to handlers
5719      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5720      */
5721     fireEvent : function(){
5722         var ce = this.events[arguments[0].toLowerCase()];
5723         if(typeof ce == "object"){
5724             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5725         }else{
5726             return true;
5727         }
5728     },
5729
5730     // private
5731     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5732
5733     /**
5734      * Appends an event handler to this component
5735      * @param {String}   eventName The type of event to listen for
5736      * @param {Function} handler The method the event invokes
5737      * @param {Object}   scope (optional) The scope in which to execute the handler
5738      * function. The handler function's "this" context.
5739      * @param {Object}   options (optional) An object containing handler configuration
5740      * properties. This may contain any of the following properties:<ul>
5741      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5742      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5743      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5744      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5745      * by the specified number of milliseconds. If the event fires again within that time, the original
5746      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5747      * </ul><br>
5748      * <p>
5749      * <b>Combining Options</b><br>
5750      * Using the options argument, it is possible to combine different types of listeners:<br>
5751      * <br>
5752      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5753                 <pre><code>
5754                 el.on('click', this.onClick, this, {
5755                         single: true,
5756                 delay: 100,
5757                 forumId: 4
5758                 });
5759                 </code></pre>
5760      * <p>
5761      * <b>Attaching multiple handlers in 1 call</b><br>
5762      * The method also allows for a single argument to be passed which is a config object containing properties
5763      * which specify multiple handlers.
5764      * <pre><code>
5765                 el.on({
5766                         'click': {
5767                         fn: this.onClick,
5768                         scope: this,
5769                         delay: 100
5770                 }, 
5771                 'mouseover': {
5772                         fn: this.onMouseOver,
5773                         scope: this
5774                 },
5775                 'mouseout': {
5776                         fn: this.onMouseOut,
5777                         scope: this
5778                 }
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * Or a shorthand syntax which passes the same scope object to all handlers:
5783         <pre><code>
5784                 el.on({
5785                         'click': this.onClick,
5786                 'mouseover': this.onMouseOver,
5787                 'mouseout': this.onMouseOut,
5788                 scope: this
5789                 });
5790                 </code></pre>
5791      */
5792     addListener : function(eventName, fn, scope, o){
5793         if(typeof eventName == "object"){
5794             o = eventName;
5795             for(var e in o){
5796                 if(this.filterOptRe.test(e)){
5797                     continue;
5798                 }
5799                 if(typeof o[e] == "function"){
5800                     // shared options
5801                     this.addListener(e, o[e], o.scope,  o);
5802                 }else{
5803                     // individual options
5804                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5805                 }
5806             }
5807             return;
5808         }
5809         o = (!o || typeof o == "boolean") ? {} : o;
5810         eventName = eventName.toLowerCase();
5811         var ce = this.events[eventName] || true;
5812         if(typeof ce == "boolean"){
5813             ce = new Roo.util.Event(this, eventName);
5814             this.events[eventName] = ce;
5815         }
5816         ce.addListener(fn, scope, o);
5817     },
5818
5819     /**
5820      * Removes a listener
5821      * @param {String}   eventName     The type of event to listen for
5822      * @param {Function} handler        The handler to remove
5823      * @param {Object}   scope  (optional) The scope (this object) for the handler
5824      */
5825     removeListener : function(eventName, fn, scope){
5826         var ce = this.events[eventName.toLowerCase()];
5827         if(typeof ce == "object"){
5828             ce.removeListener(fn, scope);
5829         }
5830     },
5831
5832     /**
5833      * Removes all listeners for this object
5834      */
5835     purgeListeners : function(){
5836         for(var evt in this.events){
5837             if(typeof this.events[evt] == "object"){
5838                  this.events[evt].clearListeners();
5839             }
5840         }
5841     },
5842
5843     relayEvents : function(o, events){
5844         var createHandler = function(ename){
5845             return function(){
5846                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5847             };
5848         };
5849         for(var i = 0, len = events.length; i < len; i++){
5850             var ename = events[i];
5851             if(!this.events[ename]){ this.events[ename] = true; };
5852             o.on(ename, createHandler(ename), this);
5853         }
5854     },
5855
5856     /**
5857      * Used to define events on this Observable
5858      * @param {Object} object The object with the events defined
5859      */
5860     addEvents : function(o){
5861         if(!this.events){
5862             this.events = {};
5863         }
5864         Roo.applyIf(this.events, o);
5865     },
5866
5867     /**
5868      * Checks to see if this object has any listeners for a specified event
5869      * @param {String} eventName The name of the event to check for
5870      * @return {Boolean} True if the event is being listened for, else false
5871      */
5872     hasListener : function(eventName){
5873         var e = this.events[eventName];
5874         return typeof e == "object" && e.listeners.length > 0;
5875     }
5876 };
5877 /**
5878  * Appends an event handler to this element (shorthand for addListener)
5879  * @param {String}   eventName     The type of event to listen for
5880  * @param {Function} handler        The method the event invokes
5881  * @param {Object}   scope (optional) The scope in which to execute the handler
5882  * function. The handler function's "this" context.
5883  * @param {Object}   options  (optional)
5884  * @method
5885  */
5886 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5887 /**
5888  * Removes a listener (shorthand for removeListener)
5889  * @param {String}   eventName     The type of event to listen for
5890  * @param {Function} handler        The handler to remove
5891  * @param {Object}   scope  (optional) The scope (this object) for the handler
5892  * @method
5893  */
5894 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5895
5896 /**
5897  * Starts capture on the specified Observable. All events will be passed
5898  * to the supplied function with the event name + standard signature of the event
5899  * <b>before</b> the event is fired. If the supplied function returns false,
5900  * the event will not fire.
5901  * @param {Observable} o The Observable to capture
5902  * @param {Function} fn The function to call
5903  * @param {Object} scope (optional) The scope (this object) for the fn
5904  * @static
5905  */
5906 Roo.util.Observable.capture = function(o, fn, scope){
5907     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5908 };
5909
5910 /**
5911  * Removes <b>all</b> added captures from the Observable.
5912  * @param {Observable} o The Observable to release
5913  * @static
5914  */
5915 Roo.util.Observable.releaseCapture = function(o){
5916     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5917 };
5918
5919 (function(){
5920
5921     var createBuffered = function(h, o, scope){
5922         var task = new Roo.util.DelayedTask();
5923         return function(){
5924             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5925         };
5926     };
5927
5928     var createSingle = function(h, e, fn, scope){
5929         return function(){
5930             e.removeListener(fn, scope);
5931             return h.apply(scope, arguments);
5932         };
5933     };
5934
5935     var createDelayed = function(h, o, scope){
5936         return function(){
5937             var args = Array.prototype.slice.call(arguments, 0);
5938             setTimeout(function(){
5939                 h.apply(scope, args);
5940             }, o.delay || 10);
5941         };
5942     };
5943
5944     Roo.util.Event = function(obj, name){
5945         this.name = name;
5946         this.obj = obj;
5947         this.listeners = [];
5948     };
5949
5950     Roo.util.Event.prototype = {
5951         addListener : function(fn, scope, options){
5952             var o = options || {};
5953             scope = scope || this.obj;
5954             if(!this.isListening(fn, scope)){
5955                 var l = {fn: fn, scope: scope, options: o};
5956                 var h = fn;
5957                 if(o.delay){
5958                     h = createDelayed(h, o, scope);
5959                 }
5960                 if(o.single){
5961                     h = createSingle(h, this, fn, scope);
5962                 }
5963                 if(o.buffer){
5964                     h = createBuffered(h, o, scope);
5965                 }
5966                 l.fireFn = h;
5967                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5968                     this.listeners.push(l);
5969                 }else{
5970                     this.listeners = this.listeners.slice(0);
5971                     this.listeners.push(l);
5972                 }
5973             }
5974         },
5975
5976         findListener : function(fn, scope){
5977             scope = scope || this.obj;
5978             var ls = this.listeners;
5979             for(var i = 0, len = ls.length; i < len; i++){
5980                 var l = ls[i];
5981                 if(l.fn == fn && l.scope == scope){
5982                     return i;
5983                 }
5984             }
5985             return -1;
5986         },
5987
5988         isListening : function(fn, scope){
5989             return this.findListener(fn, scope) != -1;
5990         },
5991
5992         removeListener : function(fn, scope){
5993             var index;
5994             if((index = this.findListener(fn, scope)) != -1){
5995                 if(!this.firing){
5996                     this.listeners.splice(index, 1);
5997                 }else{
5998                     this.listeners = this.listeners.slice(0);
5999                     this.listeners.splice(index, 1);
6000                 }
6001                 return true;
6002             }
6003             return false;
6004         },
6005
6006         clearListeners : function(){
6007             this.listeners = [];
6008         },
6009
6010         fire : function(){
6011             var ls = this.listeners, scope, len = ls.length;
6012             if(len > 0){
6013                 this.firing = true;
6014                 var args = Array.prototype.slice.call(arguments, 0);
6015                 for(var i = 0; i < len; i++){
6016                     var l = ls[i];
6017                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6018                         this.firing = false;
6019                         return false;
6020                     }
6021                 }
6022                 this.firing = false;
6023             }
6024             return true;
6025         }
6026     };
6027 })();/*
6028  * Based on:
6029  * Ext JS Library 1.1.1
6030  * Copyright(c) 2006-2007, Ext JS, LLC.
6031  *
6032  * Originally Released Under LGPL - original licence link has changed is not relivant.
6033  *
6034  * Fork - LGPL
6035  * <script type="text/javascript">
6036  */
6037
6038 /**
6039  * @class Roo.EventManager
6040  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6041  * several useful events directly.
6042  * See {@link Roo.EventObject} for more details on normalized event objects.
6043  * @singleton
6044  */
6045 Roo.EventManager = function(){
6046     var docReadyEvent, docReadyProcId, docReadyState = false;
6047     var resizeEvent, resizeTask, textEvent, textSize;
6048     var E = Roo.lib.Event;
6049     var D = Roo.lib.Dom;
6050
6051     
6052     
6053
6054     var fireDocReady = function(){
6055         if(!docReadyState){
6056             docReadyState = true;
6057             Roo.isReady = true;
6058             if(docReadyProcId){
6059                 clearInterval(docReadyProcId);
6060             }
6061             if(Roo.isGecko || Roo.isOpera) {
6062                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6063             }
6064             if(Roo.isIE){
6065                 var defer = document.getElementById("ie-deferred-loader");
6066                 if(defer){
6067                     defer.onreadystatechange = null;
6068                     defer.parentNode.removeChild(defer);
6069                 }
6070             }
6071             if(docReadyEvent){
6072                 docReadyEvent.fire();
6073                 docReadyEvent.clearListeners();
6074             }
6075         }
6076     };
6077     
6078     var initDocReady = function(){
6079         docReadyEvent = new Roo.util.Event();
6080         if(Roo.isGecko || Roo.isOpera) {
6081             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6082         }else if(Roo.isIE){
6083             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6084             var defer = document.getElementById("ie-deferred-loader");
6085             defer.onreadystatechange = function(){
6086                 if(this.readyState == "complete"){
6087                     fireDocReady();
6088                 }
6089             };
6090         }else if(Roo.isSafari){ 
6091             docReadyProcId = setInterval(function(){
6092                 var rs = document.readyState;
6093                 if(rs == "complete") {
6094                     fireDocReady();     
6095                  }
6096             }, 10);
6097         }
6098         // no matter what, make sure it fires on load
6099         E.on(window, "load", fireDocReady);
6100     };
6101
6102     var createBuffered = function(h, o){
6103         var task = new Roo.util.DelayedTask(h);
6104         return function(e){
6105             // create new event object impl so new events don't wipe out properties
6106             e = new Roo.EventObjectImpl(e);
6107             task.delay(o.buffer, h, null, [e]);
6108         };
6109     };
6110
6111     var createSingle = function(h, el, ename, fn){
6112         return function(e){
6113             Roo.EventManager.removeListener(el, ename, fn);
6114             h(e);
6115         };
6116     };
6117
6118     var createDelayed = function(h, o){
6119         return function(e){
6120             // create new event object impl so new events don't wipe out properties
6121             e = new Roo.EventObjectImpl(e);
6122             setTimeout(function(){
6123                 h(e);
6124             }, o.delay || 10);
6125         };
6126     };
6127     var transitionEndVal = false;
6128     
6129     var transitionEnd = function()
6130     {
6131         if (transitionEndVal) {
6132             return transitionEndVal;
6133         }
6134         var el = document.createElement('div');
6135
6136         var transEndEventNames = {
6137             WebkitTransition : 'webkitTransitionEnd',
6138             MozTransition    : 'transitionend',
6139             OTransition      : 'oTransitionEnd otransitionend',
6140             transition       : 'transitionend'
6141         };
6142     
6143         for (var name in transEndEventNames) {
6144             if (el.style[name] !== undefined) {
6145                 transitionEndVal = transEndEventNames[name];
6146                 return  transitionEndVal ;
6147             }
6148         }
6149     }
6150     
6151
6152     var listen = function(element, ename, opt, fn, scope){
6153         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6154         fn = fn || o.fn; scope = scope || o.scope;
6155         var el = Roo.getDom(element);
6156         
6157         
6158         if(!el){
6159             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6160         }
6161         
6162         if (ename == 'transitionend') {
6163             ename = transitionEnd();
6164         }
6165         var h = function(e){
6166             e = Roo.EventObject.setEvent(e);
6167             var t;
6168             if(o.delegate){
6169                 t = e.getTarget(o.delegate, el);
6170                 if(!t){
6171                     return;
6172                 }
6173             }else{
6174                 t = e.target;
6175             }
6176             if(o.stopEvent === true){
6177                 e.stopEvent();
6178             }
6179             if(o.preventDefault === true){
6180                e.preventDefault();
6181             }
6182             if(o.stopPropagation === true){
6183                 e.stopPropagation();
6184             }
6185
6186             if(o.normalized === false){
6187                 e = e.browserEvent;
6188             }
6189
6190             fn.call(scope || el, e, t, o);
6191         };
6192         if(o.delay){
6193             h = createDelayed(h, o);
6194         }
6195         if(o.single){
6196             h = createSingle(h, el, ename, fn);
6197         }
6198         if(o.buffer){
6199             h = createBuffered(h, o);
6200         }
6201         fn._handlers = fn._handlers || [];
6202         
6203         
6204         fn._handlers.push([Roo.id(el), ename, h]);
6205         
6206         
6207          
6208         E.on(el, ename, h);
6209         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6210             el.addEventListener("DOMMouseScroll", h, false);
6211             E.on(window, 'unload', function(){
6212                 el.removeEventListener("DOMMouseScroll", h, false);
6213             });
6214         }
6215         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6216             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6217         }
6218         return h;
6219     };
6220
6221     var stopListening = function(el, ename, fn){
6222         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6223         if(hds){
6224             for(var i = 0, len = hds.length; i < len; i++){
6225                 var h = hds[i];
6226                 if(h[0] == id && h[1] == ename){
6227                     hd = h[2];
6228                     hds.splice(i, 1);
6229                     break;
6230                 }
6231             }
6232         }
6233         E.un(el, ename, hd);
6234         el = Roo.getDom(el);
6235         if(ename == "mousewheel" && el.addEventListener){
6236             el.removeEventListener("DOMMouseScroll", hd, false);
6237         }
6238         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6239             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6240         }
6241     };
6242
6243     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6244     
6245     var pub = {
6246         
6247         
6248         /** 
6249          * Fix for doc tools
6250          * @scope Roo.EventManager
6251          */
6252         
6253         
6254         /** 
6255          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6256          * object with a Roo.EventObject
6257          * @param {Function} fn        The method the event invokes
6258          * @param {Object}   scope    An object that becomes the scope of the handler
6259          * @param {boolean}  override If true, the obj passed in becomes
6260          *                             the execution scope of the listener
6261          * @return {Function} The wrapped function
6262          * @deprecated
6263          */
6264         wrap : function(fn, scope, override){
6265             return function(e){
6266                 Roo.EventObject.setEvent(e);
6267                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6268             };
6269         },
6270         
6271         /**
6272      * Appends an event handler to an element (shorthand for addListener)
6273      * @param {String/HTMLElement}   element        The html element or id to assign the
6274      * @param {String}   eventName The type of event to listen for
6275      * @param {Function} handler The method the event invokes
6276      * @param {Object}   scope (optional) The scope in which to execute the handler
6277      * function. The handler function's "this" context.
6278      * @param {Object}   options (optional) An object containing handler configuration
6279      * properties. This may contain any of the following properties:<ul>
6280      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6281      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6282      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6283      * <li>preventDefault {Boolean} True to prevent the default action</li>
6284      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6285      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6286      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6287      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6288      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6289      * by the specified number of milliseconds. If the event fires again within that time, the original
6290      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6291      * </ul><br>
6292      * <p>
6293      * <b>Combining Options</b><br>
6294      * Using the options argument, it is possible to combine different types of listeners:<br>
6295      * <br>
6296      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6297      * Code:<pre><code>
6298 el.on('click', this.onClick, this, {
6299     single: true,
6300     delay: 100,
6301     stopEvent : true,
6302     forumId: 4
6303 });</code></pre>
6304      * <p>
6305      * <b>Attaching multiple handlers in 1 call</b><br>
6306       * The method also allows for a single argument to be passed which is a config object containing properties
6307      * which specify multiple handlers.
6308      * <p>
6309      * Code:<pre><code>
6310 el.on({
6311     'click' : {
6312         fn: this.onClick
6313         scope: this,
6314         delay: 100
6315     },
6316     'mouseover' : {
6317         fn: this.onMouseOver
6318         scope: this
6319     },
6320     'mouseout' : {
6321         fn: this.onMouseOut
6322         scope: this
6323     }
6324 });</code></pre>
6325      * <p>
6326      * Or a shorthand syntax:<br>
6327      * Code:<pre><code>
6328 el.on({
6329     'click' : this.onClick,
6330     'mouseover' : this.onMouseOver,
6331     'mouseout' : this.onMouseOut
6332     scope: this
6333 });</code></pre>
6334      */
6335         addListener : function(element, eventName, fn, scope, options){
6336             if(typeof eventName == "object"){
6337                 var o = eventName;
6338                 for(var e in o){
6339                     if(propRe.test(e)){
6340                         continue;
6341                     }
6342                     if(typeof o[e] == "function"){
6343                         // shared options
6344                         listen(element, e, o, o[e], o.scope);
6345                     }else{
6346                         // individual options
6347                         listen(element, e, o[e]);
6348                     }
6349                 }
6350                 return;
6351             }
6352             return listen(element, eventName, options, fn, scope);
6353         },
6354         
6355         /**
6356          * Removes an event handler
6357          *
6358          * @param {String/HTMLElement}   element        The id or html element to remove the 
6359          *                             event from
6360          * @param {String}   eventName     The type of event
6361          * @param {Function} fn
6362          * @return {Boolean} True if a listener was actually removed
6363          */
6364         removeListener : function(element, eventName, fn){
6365             return stopListening(element, eventName, fn);
6366         },
6367         
6368         /**
6369          * Fires when the document is ready (before onload and before images are loaded). Can be 
6370          * accessed shorthanded Roo.onReady().
6371          * @param {Function} fn        The method the event invokes
6372          * @param {Object}   scope    An  object that becomes the scope of the handler
6373          * @param {boolean}  options
6374          */
6375         onDocumentReady : function(fn, scope, options){
6376             if(docReadyState){ // if it already fired
6377                 docReadyEvent.addListener(fn, scope, options);
6378                 docReadyEvent.fire();
6379                 docReadyEvent.clearListeners();
6380                 return;
6381             }
6382             if(!docReadyEvent){
6383                 initDocReady();
6384             }
6385             docReadyEvent.addListener(fn, scope, options);
6386         },
6387         
6388         /**
6389          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6390          * @param {Function} fn        The method the event invokes
6391          * @param {Object}   scope    An object that becomes the scope of the handler
6392          * @param {boolean}  options
6393          */
6394         onWindowResize : function(fn, scope, options){
6395             if(!resizeEvent){
6396                 resizeEvent = new Roo.util.Event();
6397                 resizeTask = new Roo.util.DelayedTask(function(){
6398                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6399                 });
6400                 E.on(window, "resize", function(){
6401                     if(Roo.isIE){
6402                         resizeTask.delay(50);
6403                     }else{
6404                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6405                     }
6406                 });
6407             }
6408             resizeEvent.addListener(fn, scope, options);
6409         },
6410
6411         /**
6412          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  options
6416          */
6417         onTextResize : function(fn, scope, options){
6418             if(!textEvent){
6419                 textEvent = new Roo.util.Event();
6420                 var textEl = new Roo.Element(document.createElement('div'));
6421                 textEl.dom.className = 'x-text-resize';
6422                 textEl.dom.innerHTML = 'X';
6423                 textEl.appendTo(document.body);
6424                 textSize = textEl.dom.offsetHeight;
6425                 setInterval(function(){
6426                     if(textEl.dom.offsetHeight != textSize){
6427                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6428                     }
6429                 }, this.textResizeInterval);
6430             }
6431             textEvent.addListener(fn, scope, options);
6432         },
6433
6434         /**
6435          * Removes the passed window resize listener.
6436          * @param {Function} fn        The method the event invokes
6437          * @param {Object}   scope    The scope of handler
6438          */
6439         removeResizeListener : function(fn, scope){
6440             if(resizeEvent){
6441                 resizeEvent.removeListener(fn, scope);
6442             }
6443         },
6444
6445         // private
6446         fireResize : function(){
6447             if(resizeEvent){
6448                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6449             }   
6450         },
6451         /**
6452          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6453          */
6454         ieDeferSrc : false,
6455         /**
6456          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6457          */
6458         textResizeInterval : 50
6459     };
6460     
6461     /**
6462      * Fix for doc tools
6463      * @scopeAlias pub=Roo.EventManager
6464      */
6465     
6466      /**
6467      * Appends an event handler to an element (shorthand for addListener)
6468      * @param {String/HTMLElement}   element        The html element or id to assign the
6469      * @param {String}   eventName The type of event to listen for
6470      * @param {Function} handler The method the event invokes
6471      * @param {Object}   scope (optional) The scope in which to execute the handler
6472      * function. The handler function's "this" context.
6473      * @param {Object}   options (optional) An object containing handler configuration
6474      * properties. This may contain any of the following properties:<ul>
6475      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6476      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6477      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6478      * <li>preventDefault {Boolean} True to prevent the default action</li>
6479      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6480      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6481      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6482      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6483      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6484      * by the specified number of milliseconds. If the event fires again within that time, the original
6485      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6486      * </ul><br>
6487      * <p>
6488      * <b>Combining Options</b><br>
6489      * Using the options argument, it is possible to combine different types of listeners:<br>
6490      * <br>
6491      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6492      * Code:<pre><code>
6493 el.on('click', this.onClick, this, {
6494     single: true,
6495     delay: 100,
6496     stopEvent : true,
6497     forumId: 4
6498 });</code></pre>
6499      * <p>
6500      * <b>Attaching multiple handlers in 1 call</b><br>
6501       * The method also allows for a single argument to be passed which is a config object containing properties
6502      * which specify multiple handlers.
6503      * <p>
6504      * Code:<pre><code>
6505 el.on({
6506     'click' : {
6507         fn: this.onClick
6508         scope: this,
6509         delay: 100
6510     },
6511     'mouseover' : {
6512         fn: this.onMouseOver
6513         scope: this
6514     },
6515     'mouseout' : {
6516         fn: this.onMouseOut
6517         scope: this
6518     }
6519 });</code></pre>
6520      * <p>
6521      * Or a shorthand syntax:<br>
6522      * Code:<pre><code>
6523 el.on({
6524     'click' : this.onClick,
6525     'mouseover' : this.onMouseOver,
6526     'mouseout' : this.onMouseOut
6527     scope: this
6528 });</code></pre>
6529      */
6530     pub.on = pub.addListener;
6531     pub.un = pub.removeListener;
6532
6533     pub.stoppedMouseDownEvent = new Roo.util.Event();
6534     return pub;
6535 }();
6536 /**
6537   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6538   * @param {Function} fn        The method the event invokes
6539   * @param {Object}   scope    An  object that becomes the scope of the handler
6540   * @param {boolean}  override If true, the obj passed in becomes
6541   *                             the execution scope of the listener
6542   * @member Roo
6543   * @method onReady
6544  */
6545 Roo.onReady = Roo.EventManager.onDocumentReady;
6546
6547 Roo.onReady(function(){
6548     var bd = Roo.get(document.body);
6549     if(!bd){ return; }
6550
6551     var cls = [
6552             Roo.isIE ? "roo-ie"
6553             : Roo.isGecko ? "roo-gecko"
6554             : Roo.isOpera ? "roo-opera"
6555             : Roo.isSafari ? "roo-safari" : ""];
6556
6557     if(Roo.isMac){
6558         cls.push("roo-mac");
6559     }
6560     if(Roo.isLinux){
6561         cls.push("roo-linux");
6562     }
6563     if(Roo.isBorderBox){
6564         cls.push('roo-border-box');
6565     }
6566     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6567         var p = bd.dom.parentNode;
6568         if(p){
6569             p.className += ' roo-strict';
6570         }
6571     }
6572     bd.addClass(cls.join(' '));
6573 });
6574
6575 /**
6576  * @class Roo.EventObject
6577  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6578  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6579  * Example:
6580  * <pre><code>
6581  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6582     e.preventDefault();
6583     var target = e.getTarget();
6584     ...
6585  }
6586  var myDiv = Roo.get("myDiv");
6587  myDiv.on("click", handleClick);
6588  //or
6589  Roo.EventManager.on("myDiv", 'click', handleClick);
6590  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6591  </code></pre>
6592  * @singleton
6593  */
6594 Roo.EventObject = function(){
6595     
6596     var E = Roo.lib.Event;
6597     
6598     // safari keypress events for special keys return bad keycodes
6599     var safariKeys = {
6600         63234 : 37, // left
6601         63235 : 39, // right
6602         63232 : 38, // up
6603         63233 : 40, // down
6604         63276 : 33, // page up
6605         63277 : 34, // page down
6606         63272 : 46, // delete
6607         63273 : 36, // home
6608         63275 : 35  // end
6609     };
6610
6611     // normalize button clicks
6612     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6613                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6614
6615     Roo.EventObjectImpl = function(e){
6616         if(e){
6617             this.setEvent(e.browserEvent || e);
6618         }
6619     };
6620     Roo.EventObjectImpl.prototype = {
6621         /**
6622          * Used to fix doc tools.
6623          * @scope Roo.EventObject.prototype
6624          */
6625             
6626
6627         
6628         
6629         /** The normal browser event */
6630         browserEvent : null,
6631         /** The button pressed in a mouse event */
6632         button : -1,
6633         /** True if the shift key was down during the event */
6634         shiftKey : false,
6635         /** True if the control key was down during the event */
6636         ctrlKey : false,
6637         /** True if the alt key was down during the event */
6638         altKey : false,
6639
6640         /** Key constant 
6641         * @type Number */
6642         BACKSPACE : 8,
6643         /** Key constant 
6644         * @type Number */
6645         TAB : 9,
6646         /** Key constant 
6647         * @type Number */
6648         RETURN : 13,
6649         /** Key constant 
6650         * @type Number */
6651         ENTER : 13,
6652         /** Key constant 
6653         * @type Number */
6654         SHIFT : 16,
6655         /** Key constant 
6656         * @type Number */
6657         CONTROL : 17,
6658         /** Key constant 
6659         * @type Number */
6660         ESC : 27,
6661         /** Key constant 
6662         * @type Number */
6663         SPACE : 32,
6664         /** Key constant 
6665         * @type Number */
6666         PAGEUP : 33,
6667         /** Key constant 
6668         * @type Number */
6669         PAGEDOWN : 34,
6670         /** Key constant 
6671         * @type Number */
6672         END : 35,
6673         /** Key constant 
6674         * @type Number */
6675         HOME : 36,
6676         /** Key constant 
6677         * @type Number */
6678         LEFT : 37,
6679         /** Key constant 
6680         * @type Number */
6681         UP : 38,
6682         /** Key constant 
6683         * @type Number */
6684         RIGHT : 39,
6685         /** Key constant 
6686         * @type Number */
6687         DOWN : 40,
6688         /** Key constant 
6689         * @type Number */
6690         DELETE : 46,
6691         /** Key constant 
6692         * @type Number */
6693         F5 : 116,
6694
6695            /** @private */
6696         setEvent : function(e){
6697             if(e == this || (e && e.browserEvent)){ // already wrapped
6698                 return e;
6699             }
6700             this.browserEvent = e;
6701             if(e){
6702                 // normalize buttons
6703                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6704                 if(e.type == 'click' && this.button == -1){
6705                     this.button = 0;
6706                 }
6707                 this.type = e.type;
6708                 this.shiftKey = e.shiftKey;
6709                 // mac metaKey behaves like ctrlKey
6710                 this.ctrlKey = e.ctrlKey || e.metaKey;
6711                 this.altKey = e.altKey;
6712                 // in getKey these will be normalized for the mac
6713                 this.keyCode = e.keyCode;
6714                 // keyup warnings on firefox.
6715                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6716                 // cache the target for the delayed and or buffered events
6717                 this.target = E.getTarget(e);
6718                 // same for XY
6719                 this.xy = E.getXY(e);
6720             }else{
6721                 this.button = -1;
6722                 this.shiftKey = false;
6723                 this.ctrlKey = false;
6724                 this.altKey = false;
6725                 this.keyCode = 0;
6726                 this.charCode =0;
6727                 this.target = null;
6728                 this.xy = [0, 0];
6729             }
6730             return this;
6731         },
6732
6733         /**
6734          * Stop the event (preventDefault and stopPropagation)
6735          */
6736         stopEvent : function(){
6737             if(this.browserEvent){
6738                 if(this.browserEvent.type == 'mousedown'){
6739                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6740                 }
6741                 E.stopEvent(this.browserEvent);
6742             }
6743         },
6744
6745         /**
6746          * Prevents the browsers default handling of the event.
6747          */
6748         preventDefault : function(){
6749             if(this.browserEvent){
6750                 E.preventDefault(this.browserEvent);
6751             }
6752         },
6753
6754         /** @private */
6755         isNavKeyPress : function(){
6756             var k = this.keyCode;
6757             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6758             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6759         },
6760
6761         isSpecialKey : function(){
6762             var k = this.keyCode;
6763             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6764             (k == 16) || (k == 17) ||
6765             (k >= 18 && k <= 20) ||
6766             (k >= 33 && k <= 35) ||
6767             (k >= 36 && k <= 39) ||
6768             (k >= 44 && k <= 45);
6769         },
6770         /**
6771          * Cancels bubbling of the event.
6772          */
6773         stopPropagation : function(){
6774             if(this.browserEvent){
6775                 if(this.type == 'mousedown'){
6776                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6777                 }
6778                 E.stopPropagation(this.browserEvent);
6779             }
6780         },
6781
6782         /**
6783          * Gets the key code for the event.
6784          * @return {Number}
6785          */
6786         getCharCode : function(){
6787             return this.charCode || this.keyCode;
6788         },
6789
6790         /**
6791          * Returns a normalized keyCode for the event.
6792          * @return {Number} The key code
6793          */
6794         getKey : function(){
6795             var k = this.keyCode || this.charCode;
6796             return Roo.isSafari ? (safariKeys[k] || k) : k;
6797         },
6798
6799         /**
6800          * Gets the x coordinate of the event.
6801          * @return {Number}
6802          */
6803         getPageX : function(){
6804             return this.xy[0];
6805         },
6806
6807         /**
6808          * Gets the y coordinate of the event.
6809          * @return {Number}
6810          */
6811         getPageY : function(){
6812             return this.xy[1];
6813         },
6814
6815         /**
6816          * Gets the time of the event.
6817          * @return {Number}
6818          */
6819         getTime : function(){
6820             if(this.browserEvent){
6821                 return E.getTime(this.browserEvent);
6822             }
6823             return null;
6824         },
6825
6826         /**
6827          * Gets the page coordinates of the event.
6828          * @return {Array} The xy values like [x, y]
6829          */
6830         getXY : function(){
6831             return this.xy;
6832         },
6833
6834         /**
6835          * Gets the target for the event.
6836          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6837          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6838                 search as a number or element (defaults to 10 || document.body)
6839          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6840          * @return {HTMLelement}
6841          */
6842         getTarget : function(selector, maxDepth, returnEl){
6843             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6844         },
6845         /**
6846          * Gets the related target.
6847          * @return {HTMLElement}
6848          */
6849         getRelatedTarget : function(){
6850             if(this.browserEvent){
6851                 return E.getRelatedTarget(this.browserEvent);
6852             }
6853             return null;
6854         },
6855
6856         /**
6857          * Normalizes mouse wheel delta across browsers
6858          * @return {Number} The delta
6859          */
6860         getWheelDelta : function(){
6861             var e = this.browserEvent;
6862             var delta = 0;
6863             if(e.wheelDelta){ /* IE/Opera. */
6864                 delta = e.wheelDelta/120;
6865             }else if(e.detail){ /* Mozilla case. */
6866                 delta = -e.detail/3;
6867             }
6868             return delta;
6869         },
6870
6871         /**
6872          * Returns true if the control, meta, shift or alt key was pressed during this event.
6873          * @return {Boolean}
6874          */
6875         hasModifier : function(){
6876             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6877         },
6878
6879         /**
6880          * Returns true if the target of this event equals el or is a child of el
6881          * @param {String/HTMLElement/Element} el
6882          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6883          * @return {Boolean}
6884          */
6885         within : function(el, related){
6886             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6887             return t && Roo.fly(el).contains(t);
6888         },
6889
6890         getPoint : function(){
6891             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6892         }
6893     };
6894
6895     return new Roo.EventObjectImpl();
6896 }();
6897             
6898     /*
6899  * Based on:
6900  * Ext JS Library 1.1.1
6901  * Copyright(c) 2006-2007, Ext JS, LLC.
6902  *
6903  * Originally Released Under LGPL - original licence link has changed is not relivant.
6904  *
6905  * Fork - LGPL
6906  * <script type="text/javascript">
6907  */
6908
6909  
6910 // was in Composite Element!??!?!
6911  
6912 (function(){
6913     var D = Roo.lib.Dom;
6914     var E = Roo.lib.Event;
6915     var A = Roo.lib.Anim;
6916
6917     // local style camelizing for speed
6918     var propCache = {};
6919     var camelRe = /(-[a-z])/gi;
6920     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6921     var view = document.defaultView;
6922
6923 /**
6924  * @class Roo.Element
6925  * Represents an Element in the DOM.<br><br>
6926  * Usage:<br>
6927 <pre><code>
6928 var el = Roo.get("my-div");
6929
6930 // or with getEl
6931 var el = getEl("my-div");
6932
6933 // or with a DOM element
6934 var el = Roo.get(myDivElement);
6935 </code></pre>
6936  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6937  * each call instead of constructing a new one.<br><br>
6938  * <b>Animations</b><br />
6939  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6940  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6941 <pre>
6942 Option    Default   Description
6943 --------- --------  ---------------------------------------------
6944 duration  .35       The duration of the animation in seconds
6945 easing    easeOut   The YUI easing method
6946 callback  none      A function to execute when the anim completes
6947 scope     this      The scope (this) of the callback function
6948 </pre>
6949 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6950 * manipulate the animation. Here's an example:
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // no animation
6955 el.setWidth(100);
6956
6957 // default animation
6958 el.setWidth(100, true);
6959
6960 // animation with some options set
6961 el.setWidth(100, {
6962     duration: 1,
6963     callback: this.foo,
6964     scope: this
6965 });
6966
6967 // using the "anim" property to get the Anim object
6968 var opt = {
6969     duration: 1,
6970     callback: this.foo,
6971     scope: this
6972 };
6973 el.setWidth(100, opt);
6974 ...
6975 if(opt.anim.isAnimated()){
6976     opt.anim.stop();
6977 }
6978 </code></pre>
6979 * <b> Composite (Collections of) Elements</b><br />
6980  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6981  * @constructor Create a new Element directly.
6982  * @param {String/HTMLElement} element
6983  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6984  */
6985     Roo.Element = function(element, forceNew){
6986         var dom = typeof element == "string" ?
6987                 document.getElementById(element) : element;
6988         if(!dom){ // invalid id/element
6989             return null;
6990         }
6991         var id = dom.id;
6992         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6993             return Roo.Element.cache[id];
6994         }
6995
6996         /**
6997          * The DOM element
6998          * @type HTMLElement
6999          */
7000         this.dom = dom;
7001
7002         /**
7003          * The DOM element ID
7004          * @type String
7005          */
7006         this.id = id || Roo.id(dom);
7007     };
7008
7009     var El = Roo.Element;
7010
7011     El.prototype = {
7012         /**
7013          * The element's default display mode  (defaults to "")
7014          * @type String
7015          */
7016         originalDisplay : "",
7017
7018         visibilityMode : 1,
7019         /**
7020          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7021          * @type String
7022          */
7023         defaultUnit : "px",
7024         /**
7025          * Sets the element's visibility mode. When setVisible() is called it
7026          * will use this to determine whether to set the visibility or the display property.
7027          * @param visMode Element.VISIBILITY or Element.DISPLAY
7028          * @return {Roo.Element} this
7029          */
7030         setVisibilityMode : function(visMode){
7031             this.visibilityMode = visMode;
7032             return this;
7033         },
7034         /**
7035          * Convenience method for setVisibilityMode(Element.DISPLAY)
7036          * @param {String} display (optional) What to set display to when visible
7037          * @return {Roo.Element} this
7038          */
7039         enableDisplayMode : function(display){
7040             this.setVisibilityMode(El.DISPLAY);
7041             if(typeof display != "undefined") this.originalDisplay = display;
7042             return this;
7043         },
7044
7045         /**
7046          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7051          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7052          */
7053         findParent : function(simpleSelector, maxDepth, returnEl){
7054             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7055             maxDepth = maxDepth || 50;
7056             if(typeof maxDepth != "number"){
7057                 stopEl = Roo.getDom(maxDepth);
7058                 maxDepth = 10;
7059             }
7060             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7061                 if(dq.is(p, simpleSelector)){
7062                     return returnEl ? Roo.get(p) : p;
7063                 }
7064                 depth++;
7065                 p = p.parentNode;
7066             }
7067             return null;
7068         },
7069
7070
7071         /**
7072          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7073          * @param {String} selector The simple selector to test
7074          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7075                 search as a number or element (defaults to 10 || document.body)
7076          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7077          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7078          */
7079         findParentNode : function(simpleSelector, maxDepth, returnEl){
7080             var p = Roo.fly(this.dom.parentNode, '_internal');
7081             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7082         },
7083
7084         /**
7085          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7086          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7087          * @param {String} selector The simple selector to test
7088          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7089                 search as a number or element (defaults to 10 || document.body)
7090          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7091          */
7092         up : function(simpleSelector, maxDepth){
7093             return this.findParentNode(simpleSelector, maxDepth, true);
7094         },
7095
7096
7097
7098         /**
7099          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7100          * @param {String} selector The simple selector to test
7101          * @return {Boolean} True if this element matches the selector, else false
7102          */
7103         is : function(simpleSelector){
7104             return Roo.DomQuery.is(this.dom, simpleSelector);
7105         },
7106
7107         /**
7108          * Perform animation on this element.
7109          * @param {Object} args The YUI animation control args
7110          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7111          * @param {Function} onComplete (optional) Function to call when animation completes
7112          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7113          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7114          * @return {Roo.Element} this
7115          */
7116         animate : function(args, duration, onComplete, easing, animType){
7117             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7118             return this;
7119         },
7120
7121         /*
7122          * @private Internal animation call
7123          */
7124         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7125             animType = animType || 'run';
7126             opt = opt || {};
7127             var anim = Roo.lib.Anim[animType](
7128                 this.dom, args,
7129                 (opt.duration || defaultDur) || .35,
7130                 (opt.easing || defaultEase) || 'easeOut',
7131                 function(){
7132                     Roo.callback(cb, this);
7133                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7134                 },
7135                 this
7136             );
7137             opt.anim = anim;
7138             return anim;
7139         },
7140
7141         // private legacy anim prep
7142         preanim : function(a, i){
7143             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7144         },
7145
7146         /**
7147          * Removes worthless text nodes
7148          * @param {Boolean} forceReclean (optional) By default the element
7149          * keeps track if it has been cleaned already so
7150          * you can call this over and over. However, if you update the element and
7151          * need to force a reclean, you can pass true.
7152          */
7153         clean : function(forceReclean){
7154             if(this.isCleaned && forceReclean !== true){
7155                 return this;
7156             }
7157             var ns = /\S/;
7158             var d = this.dom, n = d.firstChild, ni = -1;
7159             while(n){
7160                 var nx = n.nextSibling;
7161                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7162                     d.removeChild(n);
7163                 }else{
7164                     n.nodeIndex = ++ni;
7165                 }
7166                 n = nx;
7167             }
7168             this.isCleaned = true;
7169             return this;
7170         },
7171
7172         // private
7173         calcOffsetsTo : function(el){
7174             el = Roo.get(el);
7175             var d = el.dom;
7176             var restorePos = false;
7177             if(el.getStyle('position') == 'static'){
7178                 el.position('relative');
7179                 restorePos = true;
7180             }
7181             var x = 0, y =0;
7182             var op = this.dom;
7183             while(op && op != d && op.tagName != 'HTML'){
7184                 x+= op.offsetLeft;
7185                 y+= op.offsetTop;
7186                 op = op.offsetParent;
7187             }
7188             if(restorePos){
7189                 el.position('static');
7190             }
7191             return [x, y];
7192         },
7193
7194         /**
7195          * Scrolls this element into view within the passed container.
7196          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7197          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7198          * @return {Roo.Element} this
7199          */
7200         scrollIntoView : function(container, hscroll){
7201             var c = Roo.getDom(container) || document.body;
7202             var el = this.dom;
7203
7204             var o = this.calcOffsetsTo(c),
7205                 l = o[0],
7206                 t = o[1],
7207                 b = t+el.offsetHeight,
7208                 r = l+el.offsetWidth;
7209
7210             var ch = c.clientHeight;
7211             var ct = parseInt(c.scrollTop, 10);
7212             var cl = parseInt(c.scrollLeft, 10);
7213             var cb = ct + ch;
7214             var cr = cl + c.clientWidth;
7215
7216             if(t < ct){
7217                 c.scrollTop = t;
7218             }else if(b > cb){
7219                 c.scrollTop = b-ch;
7220             }
7221
7222             if(hscroll !== false){
7223                 if(l < cl){
7224                     c.scrollLeft = l;
7225                 }else if(r > cr){
7226                     c.scrollLeft = r-c.clientWidth;
7227                 }
7228             }
7229             return this;
7230         },
7231
7232         // private
7233         scrollChildIntoView : function(child, hscroll){
7234             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7235         },
7236
7237         /**
7238          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7239          * the new height may not be available immediately.
7240          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7241          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7242          * @param {Function} onComplete (optional) Function to call when animation completes
7243          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7244          * @return {Roo.Element} this
7245          */
7246         autoHeight : function(animate, duration, onComplete, easing){
7247             var oldHeight = this.getHeight();
7248             this.clip();
7249             this.setHeight(1); // force clipping
7250             setTimeout(function(){
7251                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7252                 if(!animate){
7253                     this.setHeight(height);
7254                     this.unclip();
7255                     if(typeof onComplete == "function"){
7256                         onComplete();
7257                     }
7258                 }else{
7259                     this.setHeight(oldHeight); // restore original height
7260                     this.setHeight(height, animate, duration, function(){
7261                         this.unclip();
7262                         if(typeof onComplete == "function") onComplete();
7263                     }.createDelegate(this), easing);
7264                 }
7265             }.createDelegate(this), 0);
7266             return this;
7267         },
7268
7269         /**
7270          * Returns true if this element is an ancestor of the passed element
7271          * @param {HTMLElement/String} el The element to check
7272          * @return {Boolean} True if this element is an ancestor of el, else false
7273          */
7274         contains : function(el){
7275             if(!el){return false;}
7276             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7277         },
7278
7279         /**
7280          * Checks whether the element is currently visible using both visibility and display properties.
7281          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7282          * @return {Boolean} True if the element is currently visible, else false
7283          */
7284         isVisible : function(deep) {
7285             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7286             if(deep !== true || !vis){
7287                 return vis;
7288             }
7289             var p = this.dom.parentNode;
7290             while(p && p.tagName.toLowerCase() != "body"){
7291                 if(!Roo.fly(p, '_isVisible').isVisible()){
7292                     return false;
7293                 }
7294                 p = p.parentNode;
7295             }
7296             return true;
7297         },
7298
7299         /**
7300          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7301          * @param {String} selector The CSS selector
7302          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7303          * @return {CompositeElement/CompositeElementLite} The composite element
7304          */
7305         select : function(selector, unique){
7306             return El.select(selector, unique, this.dom);
7307         },
7308
7309         /**
7310          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7311          * @param {String} selector The CSS selector
7312          * @return {Array} An array of the matched nodes
7313          */
7314         query : function(selector, unique){
7315             return Roo.DomQuery.select(selector, this.dom);
7316         },
7317
7318         /**
7319          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7320          * @param {String} selector The CSS selector
7321          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7322          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7323          */
7324         child : function(selector, returnDom){
7325             var n = Roo.DomQuery.selectNode(selector, this.dom);
7326             return returnDom ? n : Roo.get(n);
7327         },
7328
7329         /**
7330          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7331          * @param {String} selector The CSS selector
7332          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7333          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7334          */
7335         down : function(selector, returnDom){
7336             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7337             return returnDom ? n : Roo.get(n);
7338         },
7339
7340         /**
7341          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7342          * @param {String} group The group the DD object is member of
7343          * @param {Object} config The DD config object
7344          * @param {Object} overrides An object containing methods to override/implement on the DD object
7345          * @return {Roo.dd.DD} The DD object
7346          */
7347         initDD : function(group, config, overrides){
7348             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7349             return Roo.apply(dd, overrides);
7350         },
7351
7352         /**
7353          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7354          * @param {String} group The group the DDProxy object is member of
7355          * @param {Object} config The DDProxy config object
7356          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7357          * @return {Roo.dd.DDProxy} The DDProxy object
7358          */
7359         initDDProxy : function(group, config, overrides){
7360             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7361             return Roo.apply(dd, overrides);
7362         },
7363
7364         /**
7365          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7366          * @param {String} group The group the DDTarget object is member of
7367          * @param {Object} config The DDTarget config object
7368          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7369          * @return {Roo.dd.DDTarget} The DDTarget object
7370          */
7371         initDDTarget : function(group, config, overrides){
7372             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7373             return Roo.apply(dd, overrides);
7374         },
7375
7376         /**
7377          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7378          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7379          * @param {Boolean} visible Whether the element is visible
7380          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7381          * @return {Roo.Element} this
7382          */
7383          setVisible : function(visible, animate){
7384             if(!animate || !A){
7385                 if(this.visibilityMode == El.DISPLAY){
7386                     this.setDisplayed(visible);
7387                 }else{
7388                     this.fixDisplay();
7389                     this.dom.style.visibility = visible ? "visible" : "hidden";
7390                 }
7391             }else{
7392                 // closure for composites
7393                 var dom = this.dom;
7394                 var visMode = this.visibilityMode;
7395                 if(visible){
7396                     this.setOpacity(.01);
7397                     this.setVisible(true);
7398                 }
7399                 this.anim({opacity: { to: (visible?1:0) }},
7400                       this.preanim(arguments, 1),
7401                       null, .35, 'easeIn', function(){
7402                          if(!visible){
7403                              if(visMode == El.DISPLAY){
7404                                  dom.style.display = "none";
7405                              }else{
7406                                  dom.style.visibility = "hidden";
7407                              }
7408                              Roo.get(dom).setOpacity(1);
7409                          }
7410                      });
7411             }
7412             return this;
7413         },
7414
7415         /**
7416          * Returns true if display is not "none"
7417          * @return {Boolean}
7418          */
7419         isDisplayed : function() {
7420             return this.getStyle("display") != "none";
7421         },
7422
7423         /**
7424          * Toggles the element's visibility or display, depending on visibility mode.
7425          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7426          * @return {Roo.Element} this
7427          */
7428         toggle : function(animate){
7429             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7430             return this;
7431         },
7432
7433         /**
7434          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7435          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7436          * @return {Roo.Element} this
7437          */
7438         setDisplayed : function(value) {
7439             if(typeof value == "boolean"){
7440                value = value ? this.originalDisplay : "none";
7441             }
7442             this.setStyle("display", value);
7443             return this;
7444         },
7445
7446         /**
7447          * Tries to focus the element. Any exceptions are caught and ignored.
7448          * @return {Roo.Element} this
7449          */
7450         focus : function() {
7451             try{
7452                 this.dom.focus();
7453             }catch(e){}
7454             return this;
7455         },
7456
7457         /**
7458          * Tries to blur the element. Any exceptions are caught and ignored.
7459          * @return {Roo.Element} this
7460          */
7461         blur : function() {
7462             try{
7463                 this.dom.blur();
7464             }catch(e){}
7465             return this;
7466         },
7467
7468         /**
7469          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7470          * @param {String/Array} className The CSS class to add, or an array of classes
7471          * @return {Roo.Element} this
7472          */
7473         addClass : function(className){
7474             if(className instanceof Array){
7475                 for(var i = 0, len = className.length; i < len; i++) {
7476                     this.addClass(className[i]);
7477                 }
7478             }else{
7479                 if(className && !this.hasClass(className)){
7480                     this.dom.className = this.dom.className + " " + className;
7481                 }
7482             }
7483             return this;
7484         },
7485
7486         /**
7487          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7488          * @param {String/Array} className The CSS class to add, or an array of classes
7489          * @return {Roo.Element} this
7490          */
7491         radioClass : function(className){
7492             var siblings = this.dom.parentNode.childNodes;
7493             for(var i = 0; i < siblings.length; i++) {
7494                 var s = siblings[i];
7495                 if(s.nodeType == 1){
7496                     Roo.get(s).removeClass(className);
7497                 }
7498             }
7499             this.addClass(className);
7500             return this;
7501         },
7502
7503         /**
7504          * Removes one or more CSS classes from the element.
7505          * @param {String/Array} className The CSS class to remove, or an array of classes
7506          * @return {Roo.Element} this
7507          */
7508         removeClass : function(className){
7509             if(!className || !this.dom.className){
7510                 return this;
7511             }
7512             if(className instanceof Array){
7513                 for(var i = 0, len = className.length; i < len; i++) {
7514                     this.removeClass(className[i]);
7515                 }
7516             }else{
7517                 if(this.hasClass(className)){
7518                     var re = this.classReCache[className];
7519                     if (!re) {
7520                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7521                        this.classReCache[className] = re;
7522                     }
7523                     this.dom.className =
7524                         this.dom.className.replace(re, " ");
7525                 }
7526             }
7527             return this;
7528         },
7529
7530         // private
7531         classReCache: {},
7532
7533         /**
7534          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7535          * @param {String} className The CSS class to toggle
7536          * @return {Roo.Element} this
7537          */
7538         toggleClass : function(className){
7539             if(this.hasClass(className)){
7540                 this.removeClass(className);
7541             }else{
7542                 this.addClass(className);
7543             }
7544             return this;
7545         },
7546
7547         /**
7548          * Checks if the specified CSS class exists on this element's DOM node.
7549          * @param {String} className The CSS class to check for
7550          * @return {Boolean} True if the class exists, else false
7551          */
7552         hasClass : function(className){
7553             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7554         },
7555
7556         /**
7557          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7558          * @param {String} oldClassName The CSS class to replace
7559          * @param {String} newClassName The replacement CSS class
7560          * @return {Roo.Element} this
7561          */
7562         replaceClass : function(oldClassName, newClassName){
7563             this.removeClass(oldClassName);
7564             this.addClass(newClassName);
7565             return this;
7566         },
7567
7568         /**
7569          * Returns an object with properties matching the styles requested.
7570          * For example, el.getStyles('color', 'font-size', 'width') might return
7571          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7572          * @param {String} style1 A style name
7573          * @param {String} style2 A style name
7574          * @param {String} etc.
7575          * @return {Object} The style object
7576          */
7577         getStyles : function(){
7578             var a = arguments, len = a.length, r = {};
7579             for(var i = 0; i < len; i++){
7580                 r[a[i]] = this.getStyle(a[i]);
7581             }
7582             return r;
7583         },
7584
7585         /**
7586          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7587          * @param {String} property The style property whose value is returned.
7588          * @return {String} The current value of the style property for this element.
7589          */
7590         getStyle : function(){
7591             return view && view.getComputedStyle ?
7592                 function(prop){
7593                     var el = this.dom, v, cs, camel;
7594                     if(prop == 'float'){
7595                         prop = "cssFloat";
7596                     }
7597                     if(el.style && (v = el.style[prop])){
7598                         return v;
7599                     }
7600                     if(cs = view.getComputedStyle(el, "")){
7601                         if(!(camel = propCache[prop])){
7602                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                         }
7604                         return cs[camel];
7605                     }
7606                     return null;
7607                 } :
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'opacity'){
7611                         if(typeof el.style.filter == 'string'){
7612                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7613                             if(m){
7614                                 var fv = parseFloat(m[1]);
7615                                 if(!isNaN(fv)){
7616                                     return fv ? fv / 100 : 0;
7617                                 }
7618                             }
7619                         }
7620                         return 1;
7621                     }else if(prop == 'float'){
7622                         prop = "styleFloat";
7623                     }
7624                     if(!(camel = propCache[prop])){
7625                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7626                     }
7627                     if(v = el.style[camel]){
7628                         return v;
7629                     }
7630                     if(cs = el.currentStyle){
7631                         return cs[camel];
7632                     }
7633                     return null;
7634                 };
7635         }(),
7636
7637         /**
7638          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7639          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7640          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7641          * @return {Roo.Element} this
7642          */
7643         setStyle : function(prop, value){
7644             if(typeof prop == "string"){
7645                 
7646                 if (prop == 'float') {
7647                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7648                     return this;
7649                 }
7650                 
7651                 var camel;
7652                 if(!(camel = propCache[prop])){
7653                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7654                 }
7655                 
7656                 if(camel == 'opacity') {
7657                     this.setOpacity(value);
7658                 }else{
7659                     this.dom.style[camel] = value;
7660                 }
7661             }else{
7662                 for(var style in prop){
7663                     if(typeof prop[style] != "function"){
7664                        this.setStyle(style, prop[style]);
7665                     }
7666                 }
7667             }
7668             return this;
7669         },
7670
7671         /**
7672          * More flexible version of {@link #setStyle} for setting style properties.
7673          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7674          * a function which returns such a specification.
7675          * @return {Roo.Element} this
7676          */
7677         applyStyles : function(style){
7678             Roo.DomHelper.applyStyles(this.dom, style);
7679             return this;
7680         },
7681
7682         /**
7683           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7684           * @return {Number} The X position of the element
7685           */
7686         getX : function(){
7687             return D.getX(this.dom);
7688         },
7689
7690         /**
7691           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7692           * @return {Number} The Y position of the element
7693           */
7694         getY : function(){
7695             return D.getY(this.dom);
7696         },
7697
7698         /**
7699           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7700           * @return {Array} The XY position of the element
7701           */
7702         getXY : function(){
7703             return D.getXY(this.dom);
7704         },
7705
7706         /**
7707          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7708          * @param {Number} The X position of the element
7709          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7710          * @return {Roo.Element} this
7711          */
7712         setX : function(x, animate){
7713             if(!animate || !A){
7714                 D.setX(this.dom, x);
7715             }else{
7716                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7717             }
7718             return this;
7719         },
7720
7721         /**
7722          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7723          * @param {Number} The Y position of the element
7724          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7725          * @return {Roo.Element} this
7726          */
7727         setY : function(y, animate){
7728             if(!animate || !A){
7729                 D.setY(this.dom, y);
7730             }else{
7731                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7732             }
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7738          * @param {String} left The left CSS property value
7739          * @return {Roo.Element} this
7740          */
7741         setLeft : function(left){
7742             this.setStyle("left", this.addUnits(left));
7743             return this;
7744         },
7745
7746         /**
7747          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7748          * @param {String} top The top CSS property value
7749          * @return {Roo.Element} this
7750          */
7751         setTop : function(top){
7752             this.setStyle("top", this.addUnits(top));
7753             return this;
7754         },
7755
7756         /**
7757          * Sets the element's CSS right style.
7758          * @param {String} right The right CSS property value
7759          * @return {Roo.Element} this
7760          */
7761         setRight : function(right){
7762             this.setStyle("right", this.addUnits(right));
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's CSS bottom style.
7768          * @param {String} bottom The bottom CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setBottom : function(bottom){
7772             this.setStyle("bottom", this.addUnits(bottom));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7778          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7779          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7780          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7781          * @return {Roo.Element} this
7782          */
7783         setXY : function(pos, animate){
7784             if(!animate || !A){
7785                 D.setXY(this.dom, pos);
7786             }else{
7787                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7788             }
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Number} x X value for new position (coordinates are page-based)
7796          * @param {Number} y Y value for new position (coordinates are page-based)
7797          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7798          * @return {Roo.Element} this
7799          */
7800         setLocation : function(x, y, animate){
7801             this.setXY([x, y], this.preanim(arguments, 2));
7802             return this;
7803         },
7804
7805         /**
7806          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7807          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7808          * @param {Number} x X value for new position (coordinates are page-based)
7809          * @param {Number} y Y value for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         moveTo : function(x, y, animate){
7814             this.setXY([x, y], this.preanim(arguments, 2));
7815             return this;
7816         },
7817
7818         /**
7819          * Returns the region of the given element.
7820          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7821          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7822          */
7823         getRegion : function(){
7824             return D.getRegion(this.dom);
7825         },
7826
7827         /**
7828          * Returns the offset height of the element
7829          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7830          * @return {Number} The element's height
7831          */
7832         getHeight : function(contentHeight){
7833             var h = this.dom.offsetHeight || 0;
7834             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7835         },
7836
7837         /**
7838          * Returns the offset width of the element
7839          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7840          * @return {Number} The element's width
7841          */
7842         getWidth : function(contentWidth){
7843             var w = this.dom.offsetWidth || 0;
7844             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7845         },
7846
7847         /**
7848          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7849          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7850          * if a height has not been set using CSS.
7851          * @return {Number}
7852          */
7853         getComputedHeight : function(){
7854             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7855             if(!h){
7856                 h = parseInt(this.getStyle('height'), 10) || 0;
7857                 if(!this.isBorderBox()){
7858                     h += this.getFrameWidth('tb');
7859                 }
7860             }
7861             return h;
7862         },
7863
7864         /**
7865          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7866          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7867          * if a width has not been set using CSS.
7868          * @return {Number}
7869          */
7870         getComputedWidth : function(){
7871             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7872             if(!w){
7873                 w = parseInt(this.getStyle('width'), 10) || 0;
7874                 if(!this.isBorderBox()){
7875                     w += this.getFrameWidth('lr');
7876                 }
7877             }
7878             return w;
7879         },
7880
7881         /**
7882          * Returns the size of the element.
7883          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7884          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7885          */
7886         getSize : function(contentSize){
7887             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7888         },
7889
7890         /**
7891          * Returns the width and height of the viewport.
7892          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7893          */
7894         getViewSize : function(){
7895             var d = this.dom, doc = document, aw = 0, ah = 0;
7896             if(d == doc || d == doc.body){
7897                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7898             }else{
7899                 return {
7900                     width : d.clientWidth,
7901                     height: d.clientHeight
7902                 };
7903             }
7904         },
7905
7906         /**
7907          * Returns the value of the "value" attribute
7908          * @param {Boolean} asNumber true to parse the value as a number
7909          * @return {String/Number}
7910          */
7911         getValue : function(asNumber){
7912             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7913         },
7914
7915         // private
7916         adjustWidth : function(width){
7917             if(typeof width == "number"){
7918                 if(this.autoBoxAdjust && !this.isBorderBox()){
7919                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7920                 }
7921                 if(width < 0){
7922                     width = 0;
7923                 }
7924             }
7925             return width;
7926         },
7927
7928         // private
7929         adjustHeight : function(height){
7930             if(typeof height == "number"){
7931                if(this.autoBoxAdjust && !this.isBorderBox()){
7932                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7933                }
7934                if(height < 0){
7935                    height = 0;
7936                }
7937             }
7938             return height;
7939         },
7940
7941         /**
7942          * Set the width of the element
7943          * @param {Number} width The new width
7944          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7945          * @return {Roo.Element} this
7946          */
7947         setWidth : function(width, animate){
7948             width = this.adjustWidth(width);
7949             if(!animate || !A){
7950                 this.dom.style.width = this.addUnits(width);
7951             }else{
7952                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7953             }
7954             return this;
7955         },
7956
7957         /**
7958          * Set the height of the element
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setHeight : function(height, animate){
7964             height = this.adjustHeight(height);
7965             if(!animate || !A){
7966                 this.dom.style.height = this.addUnits(height);
7967             }else{
7968                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7975          * @param {Number} width The new width
7976          * @param {Number} height The new height
7977          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7978          * @return {Roo.Element} this
7979          */
7980          setSize : function(width, height, animate){
7981             if(typeof width == "object"){ // in case of object from getSize()
7982                 height = width.height; width = width.width;
7983             }
7984             width = this.adjustWidth(width); height = this.adjustHeight(height);
7985             if(!animate || !A){
7986                 this.dom.style.width = this.addUnits(width);
7987                 this.dom.style.height = this.addUnits(height);
7988             }else{
7989                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7990             }
7991             return this;
7992         },
7993
7994         /**
7995          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7996          * @param {Number} x X value for new position (coordinates are page-based)
7997          * @param {Number} y Y value for new position (coordinates are page-based)
7998          * @param {Number} width The new width
7999          * @param {Number} height The new height
8000          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8001          * @return {Roo.Element} this
8002          */
8003         setBounds : function(x, y, width, height, animate){
8004             if(!animate || !A){
8005                 this.setSize(width, height);
8006                 this.setLocation(x, y);
8007             }else{
8008                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8009                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8010                               this.preanim(arguments, 4), 'motion');
8011             }
8012             return this;
8013         },
8014
8015         /**
8016          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8017          * @param {Roo.lib.Region} region The region to fill
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021         setRegion : function(region, animate){
8022             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8023             return this;
8024         },
8025
8026         /**
8027          * Appends an event handler
8028          *
8029          * @param {String}   eventName     The type of event to append
8030          * @param {Function} fn        The method the event invokes
8031          * @param {Object} scope       (optional) The scope (this object) of the fn
8032          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8033          */
8034         addListener : function(eventName, fn, scope, options){
8035             if (this.dom) {
8036                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8037             }
8038         },
8039
8040         /**
8041          * Removes an event handler from this element
8042          * @param {String} eventName the type of event to remove
8043          * @param {Function} fn the method the event invokes
8044          * @return {Roo.Element} this
8045          */
8046         removeListener : function(eventName, fn){
8047             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8048             return this;
8049         },
8050
8051         /**
8052          * Removes all previous added listeners from this element
8053          * @return {Roo.Element} this
8054          */
8055         removeAllListeners : function(){
8056             E.purgeElement(this.dom);
8057             return this;
8058         },
8059
8060         relayEvent : function(eventName, observable){
8061             this.on(eventName, function(e){
8062                 observable.fireEvent(eventName, e);
8063             });
8064         },
8065
8066         /**
8067          * Set the opacity of the element
8068          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8070          * @return {Roo.Element} this
8071          */
8072          setOpacity : function(opacity, animate){
8073             if(!animate || !A){
8074                 var s = this.dom.style;
8075                 if(Roo.isIE){
8076                     s.zoom = 1;
8077                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8078                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8079                 }else{
8080                     s.opacity = opacity;
8081                 }
8082             }else{
8083                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8084             }
8085             return this;
8086         },
8087
8088         /**
8089          * Gets the left X coordinate
8090          * @param {Boolean} local True to get the local css position instead of page coordinate
8091          * @return {Number}
8092          */
8093         getLeft : function(local){
8094             if(!local){
8095                 return this.getX();
8096             }else{
8097                 return parseInt(this.getStyle("left"), 10) || 0;
8098             }
8099         },
8100
8101         /**
8102          * Gets the right X coordinate of the element (element X position + element width)
8103          * @param {Boolean} local True to get the local css position instead of page coordinate
8104          * @return {Number}
8105          */
8106         getRight : function(local){
8107             if(!local){
8108                 return this.getX() + this.getWidth();
8109             }else{
8110                 return (this.getLeft(true) + this.getWidth()) || 0;
8111             }
8112         },
8113
8114         /**
8115          * Gets the top Y coordinate
8116          * @param {Boolean} local True to get the local css position instead of page coordinate
8117          * @return {Number}
8118          */
8119         getTop : function(local) {
8120             if(!local){
8121                 return this.getY();
8122             }else{
8123                 return parseInt(this.getStyle("top"), 10) || 0;
8124             }
8125         },
8126
8127         /**
8128          * Gets the bottom Y coordinate of the element (element Y position + element height)
8129          * @param {Boolean} local True to get the local css position instead of page coordinate
8130          * @return {Number}
8131          */
8132         getBottom : function(local){
8133             if(!local){
8134                 return this.getY() + this.getHeight();
8135             }else{
8136                 return (this.getTop(true) + this.getHeight()) || 0;
8137             }
8138         },
8139
8140         /**
8141         * Initializes positioning on this element. If a desired position is not passed, it will make the
8142         * the element positioned relative IF it is not already positioned.
8143         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8144         * @param {Number} zIndex (optional) The zIndex to apply
8145         * @param {Number} x (optional) Set the page X position
8146         * @param {Number} y (optional) Set the page Y position
8147         */
8148         position : function(pos, zIndex, x, y){
8149             if(!pos){
8150                if(this.getStyle('position') == 'static'){
8151                    this.setStyle('position', 'relative');
8152                }
8153             }else{
8154                 this.setStyle("position", pos);
8155             }
8156             if(zIndex){
8157                 this.setStyle("z-index", zIndex);
8158             }
8159             if(x !== undefined && y !== undefined){
8160                 this.setXY([x, y]);
8161             }else if(x !== undefined){
8162                 this.setX(x);
8163             }else if(y !== undefined){
8164                 this.setY(y);
8165             }
8166         },
8167
8168         /**
8169         * Clear positioning back to the default when the document was loaded
8170         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8171         * @return {Roo.Element} this
8172          */
8173         clearPositioning : function(value){
8174             value = value ||'';
8175             this.setStyle({
8176                 "left": value,
8177                 "right": value,
8178                 "top": value,
8179                 "bottom": value,
8180                 "z-index": "",
8181                 "position" : "static"
8182             });
8183             return this;
8184         },
8185
8186         /**
8187         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8188         * snapshot before performing an update and then restoring the element.
8189         * @return {Object}
8190         */
8191         getPositioning : function(){
8192             var l = this.getStyle("left");
8193             var t = this.getStyle("top");
8194             return {
8195                 "position" : this.getStyle("position"),
8196                 "left" : l,
8197                 "right" : l ? "" : this.getStyle("right"),
8198                 "top" : t,
8199                 "bottom" : t ? "" : this.getStyle("bottom"),
8200                 "z-index" : this.getStyle("z-index")
8201             };
8202         },
8203
8204         /**
8205          * Gets the width of the border(s) for the specified side(s)
8206          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8207          * passing lr would get the border (l)eft width + the border (r)ight width.
8208          * @return {Number} The width of the sides passed added together
8209          */
8210         getBorderWidth : function(side){
8211             return this.addStyles(side, El.borders);
8212         },
8213
8214         /**
8215          * Gets the width of the padding(s) for the specified side(s)
8216          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8217          * passing lr would get the padding (l)eft + the padding (r)ight.
8218          * @return {Number} The padding of the sides passed added together
8219          */
8220         getPadding : function(side){
8221             return this.addStyles(side, El.paddings);
8222         },
8223
8224         /**
8225         * Set positioning with an object returned by getPositioning().
8226         * @param {Object} posCfg
8227         * @return {Roo.Element} this
8228          */
8229         setPositioning : function(pc){
8230             this.applyStyles(pc);
8231             if(pc.right == "auto"){
8232                 this.dom.style.right = "";
8233             }
8234             if(pc.bottom == "auto"){
8235                 this.dom.style.bottom = "";
8236             }
8237             return this;
8238         },
8239
8240         // private
8241         fixDisplay : function(){
8242             if(this.getStyle("display") == "none"){
8243                 this.setStyle("visibility", "hidden");
8244                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8245                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8246                     this.setStyle("display", "block");
8247                 }
8248             }
8249         },
8250
8251         /**
8252          * Quick set left and top adding default units
8253          * @param {String} left The left CSS property value
8254          * @param {String} top The top CSS property value
8255          * @return {Roo.Element} this
8256          */
8257          setLeftTop : function(left, top){
8258             this.dom.style.left = this.addUnits(left);
8259             this.dom.style.top = this.addUnits(top);
8260             return this;
8261         },
8262
8263         /**
8264          * Move this element relative to its current position.
8265          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8266          * @param {Number} distance How far to move the element in pixels
8267          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8268          * @return {Roo.Element} this
8269          */
8270          move : function(direction, distance, animate){
8271             var xy = this.getXY();
8272             direction = direction.toLowerCase();
8273             switch(direction){
8274                 case "l":
8275                 case "left":
8276                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8277                     break;
8278                case "r":
8279                case "right":
8280                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8281                     break;
8282                case "t":
8283                case "top":
8284                case "up":
8285                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8286                     break;
8287                case "b":
8288                case "bottom":
8289                case "down":
8290                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8291                     break;
8292             }
8293             return this;
8294         },
8295
8296         /**
8297          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8298          * @return {Roo.Element} this
8299          */
8300         clip : function(){
8301             if(!this.isClipped){
8302                this.isClipped = true;
8303                this.originalClip = {
8304                    "o": this.getStyle("overflow"),
8305                    "x": this.getStyle("overflow-x"),
8306                    "y": this.getStyle("overflow-y")
8307                };
8308                this.setStyle("overflow", "hidden");
8309                this.setStyle("overflow-x", "hidden");
8310                this.setStyle("overflow-y", "hidden");
8311             }
8312             return this;
8313         },
8314
8315         /**
8316          *  Return clipping (overflow) to original clipping before clip() was called
8317          * @return {Roo.Element} this
8318          */
8319         unclip : function(){
8320             if(this.isClipped){
8321                 this.isClipped = false;
8322                 var o = this.originalClip;
8323                 if(o.o){this.setStyle("overflow", o.o);}
8324                 if(o.x){this.setStyle("overflow-x", o.x);}
8325                 if(o.y){this.setStyle("overflow-y", o.y);}
8326             }
8327             return this;
8328         },
8329
8330
8331         /**
8332          * Gets the x,y coordinates specified by the anchor position on the element.
8333          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8334          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8335          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8336          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8337          * @return {Array} [x, y] An array containing the element's x and y coordinates
8338          */
8339         getAnchorXY : function(anchor, local, s){
8340             //Passing a different size is useful for pre-calculating anchors,
8341             //especially for anchored animations that change the el size.
8342
8343             var w, h, vp = false;
8344             if(!s){
8345                 var d = this.dom;
8346                 if(d == document.body || d == document){
8347                     vp = true;
8348                     w = D.getViewWidth(); h = D.getViewHeight();
8349                 }else{
8350                     w = this.getWidth(); h = this.getHeight();
8351                 }
8352             }else{
8353                 w = s.width;  h = s.height;
8354             }
8355             var x = 0, y = 0, r = Math.round;
8356             switch((anchor || "tl").toLowerCase()){
8357                 case "c":
8358                     x = r(w*.5);
8359                     y = r(h*.5);
8360                 break;
8361                 case "t":
8362                     x = r(w*.5);
8363                     y = 0;
8364                 break;
8365                 case "l":
8366                     x = 0;
8367                     y = r(h*.5);
8368                 break;
8369                 case "r":
8370                     x = w;
8371                     y = r(h*.5);
8372                 break;
8373                 case "b":
8374                     x = r(w*.5);
8375                     y = h;
8376                 break;
8377                 case "tl":
8378                     x = 0;
8379                     y = 0;
8380                 break;
8381                 case "bl":
8382                     x = 0;
8383                     y = h;
8384                 break;
8385                 case "br":
8386                     x = w;
8387                     y = h;
8388                 break;
8389                 case "tr":
8390                     x = w;
8391                     y = 0;
8392                 break;
8393             }
8394             if(local === true){
8395                 return [x, y];
8396             }
8397             if(vp){
8398                 var sc = this.getScroll();
8399                 return [x + sc.left, y + sc.top];
8400             }
8401             //Add the element's offset xy
8402             var o = this.getXY();
8403             return [x+o[0], y+o[1]];
8404         },
8405
8406         /**
8407          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8408          * supported position values.
8409          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8410          * @param {String} position The position to align to.
8411          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8412          * @return {Array} [x, y]
8413          */
8414         getAlignToXY : function(el, p, o){
8415             el = Roo.get(el);
8416             var d = this.dom;
8417             if(!el.dom){
8418                 throw "Element.alignTo with an element that doesn't exist";
8419             }
8420             var c = false; //constrain to viewport
8421             var p1 = "", p2 = "";
8422             o = o || [0,0];
8423
8424             if(!p){
8425                 p = "tl-bl";
8426             }else if(p == "?"){
8427                 p = "tl-bl?";
8428             }else if(p.indexOf("-") == -1){
8429                 p = "tl-" + p;
8430             }
8431             p = p.toLowerCase();
8432             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8433             if(!m){
8434                throw "Element.alignTo with an invalid alignment " + p;
8435             }
8436             p1 = m[1]; p2 = m[2]; c = !!m[3];
8437
8438             //Subtract the aligned el's internal xy from the target's offset xy
8439             //plus custom offset to get the aligned el's new offset xy
8440             var a1 = this.getAnchorXY(p1, true);
8441             var a2 = el.getAnchorXY(p2, false);
8442             var x = a2[0] - a1[0] + o[0];
8443             var y = a2[1] - a1[1] + o[1];
8444             if(c){
8445                 //constrain the aligned el to viewport if necessary
8446                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8447                 // 5px of margin for ie
8448                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8449
8450                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8451                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8452                 //otherwise swap the aligned el to the opposite border of the target.
8453                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8454                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8455                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8456                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8457
8458                var doc = document;
8459                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8460                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8461
8462                if((x+w) > dw + scrollX){
8463                     x = swapX ? r.left-w : dw+scrollX-w;
8464                 }
8465                if(x < scrollX){
8466                    x = swapX ? r.right : scrollX;
8467                }
8468                if((y+h) > dh + scrollY){
8469                     y = swapY ? r.top-h : dh+scrollY-h;
8470                 }
8471                if (y < scrollY){
8472                    y = swapY ? r.bottom : scrollY;
8473                }
8474             }
8475             return [x,y];
8476         },
8477
8478         // private
8479         getConstrainToXY : function(){
8480             var os = {top:0, left:0, bottom:0, right: 0};
8481
8482             return function(el, local, offsets, proposedXY){
8483                 el = Roo.get(el);
8484                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8485
8486                 var vw, vh, vx = 0, vy = 0;
8487                 if(el.dom == document.body || el.dom == document){
8488                     vw = Roo.lib.Dom.getViewWidth();
8489                     vh = Roo.lib.Dom.getViewHeight();
8490                 }else{
8491                     vw = el.dom.clientWidth;
8492                     vh = el.dom.clientHeight;
8493                     if(!local){
8494                         var vxy = el.getXY();
8495                         vx = vxy[0];
8496                         vy = vxy[1];
8497                     }
8498                 }
8499
8500                 var s = el.getScroll();
8501
8502                 vx += offsets.left + s.left;
8503                 vy += offsets.top + s.top;
8504
8505                 vw -= offsets.right;
8506                 vh -= offsets.bottom;
8507
8508                 var vr = vx+vw;
8509                 var vb = vy+vh;
8510
8511                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8512                 var x = xy[0], y = xy[1];
8513                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8514
8515                 // only move it if it needs it
8516                 var moved = false;
8517
8518                 // first validate right/bottom
8519                 if((x + w) > vr){
8520                     x = vr - w;
8521                     moved = true;
8522                 }
8523                 if((y + h) > vb){
8524                     y = vb - h;
8525                     moved = true;
8526                 }
8527                 // then make sure top/left isn't negative
8528                 if(x < vx){
8529                     x = vx;
8530                     moved = true;
8531                 }
8532                 if(y < vy){
8533                     y = vy;
8534                     moved = true;
8535                 }
8536                 return moved ? [x, y] : false;
8537             };
8538         }(),
8539
8540         // private
8541         adjustForConstraints : function(xy, parent, offsets){
8542             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8543         },
8544
8545         /**
8546          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8547          * document it aligns it to the viewport.
8548          * The position parameter is optional, and can be specified in any one of the following formats:
8549          * <ul>
8550          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8551          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8552          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8553          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8554          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8555          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8556          * </ul>
8557          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8558          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8559          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8560          * that specified in order to enforce the viewport constraints.
8561          * Following are all of the supported anchor positions:
8562     <pre>
8563     Value  Description
8564     -----  -----------------------------
8565     tl     The top left corner (default)
8566     t      The center of the top edge
8567     tr     The top right corner
8568     l      The center of the left edge
8569     c      In the center of the element
8570     r      The center of the right edge
8571     bl     The bottom left corner
8572     b      The center of the bottom edge
8573     br     The bottom right corner
8574     </pre>
8575     Example Usage:
8576     <pre><code>
8577     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8578     el.alignTo("other-el");
8579
8580     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8581     el.alignTo("other-el", "tr?");
8582
8583     // align the bottom right corner of el with the center left edge of other-el
8584     el.alignTo("other-el", "br-l?");
8585
8586     // align the center of el with the bottom left corner of other-el and
8587     // adjust the x position by -6 pixels (and the y position by 0)
8588     el.alignTo("other-el", "c-bl", [-6, 0]);
8589     </code></pre>
8590          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8591          * @param {String} position The position to align to.
8592          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8593          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8594          * @return {Roo.Element} this
8595          */
8596         alignTo : function(element, position, offsets, animate){
8597             var xy = this.getAlignToXY(element, position, offsets);
8598             this.setXY(xy, this.preanim(arguments, 3));
8599             return this;
8600         },
8601
8602         /**
8603          * Anchors an element to another element and realigns it when the window is resized.
8604          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8605          * @param {String} position The position to align to.
8606          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8607          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8608          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8609          * is a number, it is used as the buffer delay (defaults to 50ms).
8610          * @param {Function} callback The function to call after the animation finishes
8611          * @return {Roo.Element} this
8612          */
8613         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8614             var action = function(){
8615                 this.alignTo(el, alignment, offsets, animate);
8616                 Roo.callback(callback, this);
8617             };
8618             Roo.EventManager.onWindowResize(action, this);
8619             var tm = typeof monitorScroll;
8620             if(tm != 'undefined'){
8621                 Roo.EventManager.on(window, 'scroll', action, this,
8622                     {buffer: tm == 'number' ? monitorScroll : 50});
8623             }
8624             action.call(this); // align immediately
8625             return this;
8626         },
8627         /**
8628          * Clears any opacity settings from this element. Required in some cases for IE.
8629          * @return {Roo.Element} this
8630          */
8631         clearOpacity : function(){
8632             if (window.ActiveXObject) {
8633                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8634                     this.dom.style.filter = "";
8635                 }
8636             } else {
8637                 this.dom.style.opacity = "";
8638                 this.dom.style["-moz-opacity"] = "";
8639                 this.dom.style["-khtml-opacity"] = "";
8640             }
8641             return this;
8642         },
8643
8644         /**
8645          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8646          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8647          * @return {Roo.Element} this
8648          */
8649         hide : function(animate){
8650             this.setVisible(false, this.preanim(arguments, 0));
8651             return this;
8652         },
8653
8654         /**
8655         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8656         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         show : function(animate){
8660             this.setVisible(true, this.preanim(arguments, 0));
8661             return this;
8662         },
8663
8664         /**
8665          * @private Test if size has a unit, otherwise appends the default
8666          */
8667         addUnits : function(size){
8668             return Roo.Element.addUnits(size, this.defaultUnit);
8669         },
8670
8671         /**
8672          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8673          * @return {Roo.Element} this
8674          */
8675         beginMeasure : function(){
8676             var el = this.dom;
8677             if(el.offsetWidth || el.offsetHeight){
8678                 return this; // offsets work already
8679             }
8680             var changed = [];
8681             var p = this.dom, b = document.body; // start with this element
8682             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8683                 var pe = Roo.get(p);
8684                 if(pe.getStyle('display') == 'none'){
8685                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8686                     p.style.visibility = "hidden";
8687                     p.style.display = "block";
8688                 }
8689                 p = p.parentNode;
8690             }
8691             this._measureChanged = changed;
8692             return this;
8693
8694         },
8695
8696         /**
8697          * Restores displays to before beginMeasure was called
8698          * @return {Roo.Element} this
8699          */
8700         endMeasure : function(){
8701             var changed = this._measureChanged;
8702             if(changed){
8703                 for(var i = 0, len = changed.length; i < len; i++) {
8704                     var r = changed[i];
8705                     r.el.style.visibility = r.visibility;
8706                     r.el.style.display = "none";
8707                 }
8708                 this._measureChanged = null;
8709             }
8710             return this;
8711         },
8712
8713         /**
8714         * Update the innerHTML of this element, optionally searching for and processing scripts
8715         * @param {String} html The new HTML
8716         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8717         * @param {Function} callback For async script loading you can be noticed when the update completes
8718         * @return {Roo.Element} this
8719          */
8720         update : function(html, loadScripts, callback){
8721             if(typeof html == "undefined"){
8722                 html = "";
8723             }
8724             if(loadScripts !== true){
8725                 this.dom.innerHTML = html;
8726                 if(typeof callback == "function"){
8727                     callback();
8728                 }
8729                 return this;
8730             }
8731             var id = Roo.id();
8732             var dom = this.dom;
8733
8734             html += '<span id="' + id + '"></span>';
8735
8736             E.onAvailable(id, function(){
8737                 var hd = document.getElementsByTagName("head")[0];
8738                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8739                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8740                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8741
8742                 var match;
8743                 while(match = re.exec(html)){
8744                     var attrs = match[1];
8745                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8746                     if(srcMatch && srcMatch[2]){
8747                        var s = document.createElement("script");
8748                        s.src = srcMatch[2];
8749                        var typeMatch = attrs.match(typeRe);
8750                        if(typeMatch && typeMatch[2]){
8751                            s.type = typeMatch[2];
8752                        }
8753                        hd.appendChild(s);
8754                     }else if(match[2] && match[2].length > 0){
8755                         if(window.execScript) {
8756                            window.execScript(match[2]);
8757                         } else {
8758                             /**
8759                              * eval:var:id
8760                              * eval:var:dom
8761                              * eval:var:html
8762                              * 
8763                              */
8764                            window.eval(match[2]);
8765                         }
8766                     }
8767                 }
8768                 var el = document.getElementById(id);
8769                 if(el){el.parentNode.removeChild(el);}
8770                 if(typeof callback == "function"){
8771                     callback();
8772                 }
8773             });
8774             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8775             return this;
8776         },
8777
8778         /**
8779          * Direct access to the UpdateManager update() method (takes the same parameters).
8780          * @param {String/Function} url The url for this request or a function to call to get the url
8781          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8782          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8783          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8784          * @return {Roo.Element} this
8785          */
8786         load : function(){
8787             var um = this.getUpdateManager();
8788             um.update.apply(um, arguments);
8789             return this;
8790         },
8791
8792         /**
8793         * Gets this element's UpdateManager
8794         * @return {Roo.UpdateManager} The UpdateManager
8795         */
8796         getUpdateManager : function(){
8797             if(!this.updateManager){
8798                 this.updateManager = new Roo.UpdateManager(this);
8799             }
8800             return this.updateManager;
8801         },
8802
8803         /**
8804          * Disables text selection for this element (normalized across browsers)
8805          * @return {Roo.Element} this
8806          */
8807         unselectable : function(){
8808             this.dom.unselectable = "on";
8809             this.swallowEvent("selectstart", true);
8810             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8811             this.addClass("x-unselectable");
8812             return this;
8813         },
8814
8815         /**
8816         * Calculates the x, y to center this element on the screen
8817         * @return {Array} The x, y values [x, y]
8818         */
8819         getCenterXY : function(){
8820             return this.getAlignToXY(document, 'c-c');
8821         },
8822
8823         /**
8824         * Centers the Element in either the viewport, or another Element.
8825         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8826         */
8827         center : function(centerIn){
8828             this.alignTo(centerIn || document, 'c-c');
8829             return this;
8830         },
8831
8832         /**
8833          * Tests various css rules/browsers to determine if this element uses a border box
8834          * @return {Boolean}
8835          */
8836         isBorderBox : function(){
8837             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8838         },
8839
8840         /**
8841          * Return a box {x, y, width, height} that can be used to set another elements
8842          * size/location to match this element.
8843          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8844          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8845          * @return {Object} box An object in the format {x, y, width, height}
8846          */
8847         getBox : function(contentBox, local){
8848             var xy;
8849             if(!local){
8850                 xy = this.getXY();
8851             }else{
8852                 var left = parseInt(this.getStyle("left"), 10) || 0;
8853                 var top = parseInt(this.getStyle("top"), 10) || 0;
8854                 xy = [left, top];
8855             }
8856             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8857             if(!contentBox){
8858                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8859             }else{
8860                 var l = this.getBorderWidth("l")+this.getPadding("l");
8861                 var r = this.getBorderWidth("r")+this.getPadding("r");
8862                 var t = this.getBorderWidth("t")+this.getPadding("t");
8863                 var b = this.getBorderWidth("b")+this.getPadding("b");
8864                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8865             }
8866             bx.right = bx.x + bx.width;
8867             bx.bottom = bx.y + bx.height;
8868             return bx;
8869         },
8870
8871         /**
8872          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8873          for more information about the sides.
8874          * @param {String} sides
8875          * @return {Number}
8876          */
8877         getFrameWidth : function(sides, onlyContentBox){
8878             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8879         },
8880
8881         /**
8882          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8883          * @param {Object} box The box to fill {x, y, width, height}
8884          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8885          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8886          * @return {Roo.Element} this
8887          */
8888         setBox : function(box, adjust, animate){
8889             var w = box.width, h = box.height;
8890             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8891                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8892                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8893             }
8894             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8895             return this;
8896         },
8897
8898         /**
8899          * Forces the browser to repaint this element
8900          * @return {Roo.Element} this
8901          */
8902          repaint : function(){
8903             var dom = this.dom;
8904             this.addClass("x-repaint");
8905             setTimeout(function(){
8906                 Roo.get(dom).removeClass("x-repaint");
8907             }, 1);
8908             return this;
8909         },
8910
8911         /**
8912          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8913          * then it returns the calculated width of the sides (see getPadding)
8914          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8915          * @return {Object/Number}
8916          */
8917         getMargins : function(side){
8918             if(!side){
8919                 return {
8920                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8921                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8922                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8923                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8924                 };
8925             }else{
8926                 return this.addStyles(side, El.margins);
8927              }
8928         },
8929
8930         // private
8931         addStyles : function(sides, styles){
8932             var val = 0, v, w;
8933             for(var i = 0, len = sides.length; i < len; i++){
8934                 v = this.getStyle(styles[sides.charAt(i)]);
8935                 if(v){
8936                      w = parseInt(v, 10);
8937                      if(w){ val += w; }
8938                 }
8939             }
8940             return val;
8941         },
8942
8943         /**
8944          * Creates a proxy element of this element
8945          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8946          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8947          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8948          * @return {Roo.Element} The new proxy element
8949          */
8950         createProxy : function(config, renderTo, matchBox){
8951             if(renderTo){
8952                 renderTo = Roo.getDom(renderTo);
8953             }else{
8954                 renderTo = document.body;
8955             }
8956             config = typeof config == "object" ?
8957                 config : {tag : "div", cls: config};
8958             var proxy = Roo.DomHelper.append(renderTo, config, true);
8959             if(matchBox){
8960                proxy.setBox(this.getBox());
8961             }
8962             return proxy;
8963         },
8964
8965         /**
8966          * Puts a mask over this element to disable user interaction. Requires core.css.
8967          * This method can only be applied to elements which accept child nodes.
8968          * @param {String} msg (optional) A message to display in the mask
8969          * @param {String} msgCls (optional) A css class to apply to the msg element
8970          * @return {Element} The mask  element
8971          */
8972         mask : function(msg, msgCls)
8973         {
8974             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8975                 this.setStyle("position", "relative");
8976             }
8977             if(!this._mask){
8978                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8979             }
8980             this.addClass("x-masked");
8981             this._mask.setDisplayed(true);
8982             
8983             // we wander
8984             var z = 0;
8985             var dom = this.dom
8986             while (dom && dom.style) {
8987                 if (!isNaN(parseInt(dom.style.zIndex))) {
8988                     z = Math.max(z, parseInt(dom.style.zIndex));
8989                 }
8990                 dom = dom.parentNode;
8991             }
8992             // if we are masking the body - then it hides everything..
8993             if (this.dom == document.body) {
8994                 z = 1000000;
8995                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8996                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8997             }
8998            
8999             if(typeof msg == 'string'){
9000                 if(!this._maskMsg){
9001                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9002                 }
9003                 var mm = this._maskMsg;
9004                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9005                 mm.dom.firstChild.innerHTML = msg;
9006                 mm.setDisplayed(true);
9007                 mm.center(this);
9008                 mm.setStyle('z-index', z + 102);
9009             }
9010             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9011                 this._mask.setHeight(this.getHeight());
9012             }
9013             this._mask.setStyle('z-index', z + 100);
9014             
9015             return this._mask;
9016         },
9017
9018         /**
9019          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9020          * it is cached for reuse.
9021          */
9022         unmask : function(removeEl){
9023             if(this._mask){
9024                 if(removeEl === true){
9025                     this._mask.remove();
9026                     delete this._mask;
9027                     if(this._maskMsg){
9028                         this._maskMsg.remove();
9029                         delete this._maskMsg;
9030                     }
9031                 }else{
9032                     this._mask.setDisplayed(false);
9033                     if(this._maskMsg){
9034                         this._maskMsg.setDisplayed(false);
9035                     }
9036                 }
9037             }
9038             this.removeClass("x-masked");
9039         },
9040
9041         /**
9042          * Returns true if this element is masked
9043          * @return {Boolean}
9044          */
9045         isMasked : function(){
9046             return this._mask && this._mask.isVisible();
9047         },
9048
9049         /**
9050          * Creates an iframe shim for this element to keep selects and other windowed objects from
9051          * showing through.
9052          * @return {Roo.Element} The new shim element
9053          */
9054         createShim : function(){
9055             var el = document.createElement('iframe');
9056             el.frameBorder = 'no';
9057             el.className = 'roo-shim';
9058             if(Roo.isIE && Roo.isSecure){
9059                 el.src = Roo.SSL_SECURE_URL;
9060             }
9061             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9062             shim.autoBoxAdjust = false;
9063             return shim;
9064         },
9065
9066         /**
9067          * Removes this element from the DOM and deletes it from the cache
9068          */
9069         remove : function(){
9070             if(this.dom.parentNode){
9071                 this.dom.parentNode.removeChild(this.dom);
9072             }
9073             delete El.cache[this.dom.id];
9074         },
9075
9076         /**
9077          * Sets up event handlers to add and remove a css class when the mouse is over this element
9078          * @param {String} className
9079          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9080          * mouseout events for children elements
9081          * @return {Roo.Element} this
9082          */
9083         addClassOnOver : function(className, preventFlicker){
9084             this.on("mouseover", function(){
9085                 Roo.fly(this, '_internal').addClass(className);
9086             }, this.dom);
9087             var removeFn = function(e){
9088                 if(preventFlicker !== true || !e.within(this, true)){
9089                     Roo.fly(this, '_internal').removeClass(className);
9090                 }
9091             };
9092             this.on("mouseout", removeFn, this.dom);
9093             return this;
9094         },
9095
9096         /**
9097          * Sets up event handlers to add and remove a css class when this element has the focus
9098          * @param {String} className
9099          * @return {Roo.Element} this
9100          */
9101         addClassOnFocus : function(className){
9102             this.on("focus", function(){
9103                 Roo.fly(this, '_internal').addClass(className);
9104             }, this.dom);
9105             this.on("blur", function(){
9106                 Roo.fly(this, '_internal').removeClass(className);
9107             }, this.dom);
9108             return this;
9109         },
9110         /**
9111          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9112          * @param {String} className
9113          * @return {Roo.Element} this
9114          */
9115         addClassOnClick : function(className){
9116             var dom = this.dom;
9117             this.on("mousedown", function(){
9118                 Roo.fly(dom, '_internal').addClass(className);
9119                 var d = Roo.get(document);
9120                 var fn = function(){
9121                     Roo.fly(dom, '_internal').removeClass(className);
9122                     d.removeListener("mouseup", fn);
9123                 };
9124                 d.on("mouseup", fn);
9125             });
9126             return this;
9127         },
9128
9129         /**
9130          * Stops the specified event from bubbling and optionally prevents the default action
9131          * @param {String} eventName
9132          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9133          * @return {Roo.Element} this
9134          */
9135         swallowEvent : function(eventName, preventDefault){
9136             var fn = function(e){
9137                 e.stopPropagation();
9138                 if(preventDefault){
9139                     e.preventDefault();
9140                 }
9141             };
9142             if(eventName instanceof Array){
9143                 for(var i = 0, len = eventName.length; i < len; i++){
9144                      this.on(eventName[i], fn);
9145                 }
9146                 return this;
9147             }
9148             this.on(eventName, fn);
9149             return this;
9150         },
9151
9152         /**
9153          * @private
9154          */
9155       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9156
9157         /**
9158          * Sizes this element to its parent element's dimensions performing
9159          * neccessary box adjustments.
9160          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9161          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9162          * @return {Roo.Element} this
9163          */
9164         fitToParent : function(monitorResize, targetParent) {
9165           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9166           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9167           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9168             return;
9169           }
9170           var p = Roo.get(targetParent || this.dom.parentNode);
9171           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9172           if (monitorResize === true) {
9173             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9174             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9175           }
9176           return this;
9177         },
9178
9179         /**
9180          * Gets the next sibling, skipping text nodes
9181          * @return {HTMLElement} The next sibling or null
9182          */
9183         getNextSibling : function(){
9184             var n = this.dom.nextSibling;
9185             while(n && n.nodeType != 1){
9186                 n = n.nextSibling;
9187             }
9188             return n;
9189         },
9190
9191         /**
9192          * Gets the previous sibling, skipping text nodes
9193          * @return {HTMLElement} The previous sibling or null
9194          */
9195         getPrevSibling : function(){
9196             var n = this.dom.previousSibling;
9197             while(n && n.nodeType != 1){
9198                 n = n.previousSibling;
9199             }
9200             return n;
9201         },
9202
9203
9204         /**
9205          * Appends the passed element(s) to this element
9206          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9207          * @return {Roo.Element} this
9208          */
9209         appendChild: function(el){
9210             el = Roo.get(el);
9211             el.appendTo(this);
9212             return this;
9213         },
9214
9215         /**
9216          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9217          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9218          * automatically generated with the specified attributes.
9219          * @param {HTMLElement} insertBefore (optional) a child element of this element
9220          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9221          * @return {Roo.Element} The new child element
9222          */
9223         createChild: function(config, insertBefore, returnDom){
9224             config = config || {tag:'div'};
9225             if(insertBefore){
9226                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9227             }
9228             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9229         },
9230
9231         /**
9232          * Appends this element to the passed element
9233          * @param {String/HTMLElement/Element} el The new parent element
9234          * @return {Roo.Element} this
9235          */
9236         appendTo: function(el){
9237             el = Roo.getDom(el);
9238             el.appendChild(this.dom);
9239             return this;
9240         },
9241
9242         /**
9243          * Inserts this element before the passed element in the DOM
9244          * @param {String/HTMLElement/Element} el The element to insert before
9245          * @return {Roo.Element} this
9246          */
9247         insertBefore: function(el){
9248             el = Roo.getDom(el);
9249             el.parentNode.insertBefore(this.dom, el);
9250             return this;
9251         },
9252
9253         /**
9254          * Inserts this element after the passed element in the DOM
9255          * @param {String/HTMLElement/Element} el The element to insert after
9256          * @return {Roo.Element} this
9257          */
9258         insertAfter: function(el){
9259             el = Roo.getDom(el);
9260             el.parentNode.insertBefore(this.dom, el.nextSibling);
9261             return this;
9262         },
9263
9264         /**
9265          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9266          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9267          * @return {Roo.Element} The new child
9268          */
9269         insertFirst: function(el, returnDom){
9270             el = el || {};
9271             if(typeof el == 'object' && !el.nodeType){ // dh config
9272                 return this.createChild(el, this.dom.firstChild, returnDom);
9273             }else{
9274                 el = Roo.getDom(el);
9275                 this.dom.insertBefore(el, this.dom.firstChild);
9276                 return !returnDom ? Roo.get(el) : el;
9277             }
9278         },
9279
9280         /**
9281          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9282          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9283          * @param {String} where (optional) 'before' or 'after' defaults to before
9284          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9285          * @return {Roo.Element} the inserted Element
9286          */
9287         insertSibling: function(el, where, returnDom){
9288             where = where ? where.toLowerCase() : 'before';
9289             el = el || {};
9290             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9291
9292             if(typeof el == 'object' && !el.nodeType){ // dh config
9293                 if(where == 'after' && !this.dom.nextSibling){
9294                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9295                 }else{
9296                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9297                 }
9298
9299             }else{
9300                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9301                             where == 'before' ? this.dom : this.dom.nextSibling);
9302                 if(!returnDom){
9303                     rt = Roo.get(rt);
9304                 }
9305             }
9306             return rt;
9307         },
9308
9309         /**
9310          * Creates and wraps this element with another element
9311          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9312          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9313          * @return {HTMLElement/Element} The newly created wrapper element
9314          */
9315         wrap: function(config, returnDom){
9316             if(!config){
9317                 config = {tag: "div"};
9318             }
9319             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9320             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9321             return newEl;
9322         },
9323
9324         /**
9325          * Replaces the passed element with this element
9326          * @param {String/HTMLElement/Element} el The element to replace
9327          * @return {Roo.Element} this
9328          */
9329         replace: function(el){
9330             el = Roo.get(el);
9331             this.insertBefore(el);
9332             el.remove();
9333             return this;
9334         },
9335
9336         /**
9337          * Inserts an html fragment into this element
9338          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9339          * @param {String} html The HTML fragment
9340          * @param {Boolean} returnEl True to return an Roo.Element
9341          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9342          */
9343         insertHtml : function(where, html, returnEl){
9344             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9345             return returnEl ? Roo.get(el) : el;
9346         },
9347
9348         /**
9349          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9350          * @param {Object} o The object with the attributes
9351          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9352          * @return {Roo.Element} this
9353          */
9354         set : function(o, useSet){
9355             var el = this.dom;
9356             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9357             for(var attr in o){
9358                 if(attr == "style" || typeof o[attr] == "function") continue;
9359                 if(attr=="cls"){
9360                     el.className = o["cls"];
9361                 }else{
9362                     if(useSet) el.setAttribute(attr, o[attr]);
9363                     else el[attr] = o[attr];
9364                 }
9365             }
9366             if(o.style){
9367                 Roo.DomHelper.applyStyles(el, o.style);
9368             }
9369             return this;
9370         },
9371
9372         /**
9373          * Convenience method for constructing a KeyMap
9374          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9375          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9376          * @param {Function} fn The function to call
9377          * @param {Object} scope (optional) The scope of the function
9378          * @return {Roo.KeyMap} The KeyMap created
9379          */
9380         addKeyListener : function(key, fn, scope){
9381             var config;
9382             if(typeof key != "object" || key instanceof Array){
9383                 config = {
9384                     key: key,
9385                     fn: fn,
9386                     scope: scope
9387                 };
9388             }else{
9389                 config = {
9390                     key : key.key,
9391                     shift : key.shift,
9392                     ctrl : key.ctrl,
9393                     alt : key.alt,
9394                     fn: fn,
9395                     scope: scope
9396                 };
9397             }
9398             return new Roo.KeyMap(this, config);
9399         },
9400
9401         /**
9402          * Creates a KeyMap for this element
9403          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9404          * @return {Roo.KeyMap} The KeyMap created
9405          */
9406         addKeyMap : function(config){
9407             return new Roo.KeyMap(this, config);
9408         },
9409
9410         /**
9411          * Returns true if this element is scrollable.
9412          * @return {Boolean}
9413          */
9414          isScrollable : function(){
9415             var dom = this.dom;
9416             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9417         },
9418
9419         /**
9420          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9421          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9422          * @param {Number} value The new scroll value
9423          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9424          * @return {Element} this
9425          */
9426
9427         scrollTo : function(side, value, animate){
9428             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9429             if(!animate || !A){
9430                 this.dom[prop] = value;
9431             }else{
9432                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9433                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9434             }
9435             return this;
9436         },
9437
9438         /**
9439          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9440          * within this element's scrollable range.
9441          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9442          * @param {Number} distance How far to scroll the element in pixels
9443          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9444          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9445          * was scrolled as far as it could go.
9446          */
9447          scroll : function(direction, distance, animate){
9448              if(!this.isScrollable()){
9449                  return;
9450              }
9451              var el = this.dom;
9452              var l = el.scrollLeft, t = el.scrollTop;
9453              var w = el.scrollWidth, h = el.scrollHeight;
9454              var cw = el.clientWidth, ch = el.clientHeight;
9455              direction = direction.toLowerCase();
9456              var scrolled = false;
9457              var a = this.preanim(arguments, 2);
9458              switch(direction){
9459                  case "l":
9460                  case "left":
9461                      if(w - l > cw){
9462                          var v = Math.min(l + distance, w-cw);
9463                          this.scrollTo("left", v, a);
9464                          scrolled = true;
9465                      }
9466                      break;
9467                 case "r":
9468                 case "right":
9469                      if(l > 0){
9470                          var v = Math.max(l - distance, 0);
9471                          this.scrollTo("left", v, a);
9472                          scrolled = true;
9473                      }
9474                      break;
9475                 case "t":
9476                 case "top":
9477                 case "up":
9478                      if(t > 0){
9479                          var v = Math.max(t - distance, 0);
9480                          this.scrollTo("top", v, a);
9481                          scrolled = true;
9482                      }
9483                      break;
9484                 case "b":
9485                 case "bottom":
9486                 case "down":
9487                      if(h - t > ch){
9488                          var v = Math.min(t + distance, h-ch);
9489                          this.scrollTo("top", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493              }
9494              return scrolled;
9495         },
9496
9497         /**
9498          * Translates the passed page coordinates into left/top css values for this element
9499          * @param {Number/Array} x The page x or an array containing [x, y]
9500          * @param {Number} y The page y
9501          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9502          */
9503         translatePoints : function(x, y){
9504             if(typeof x == 'object' || x instanceof Array){
9505                 y = x[1]; x = x[0];
9506             }
9507             var p = this.getStyle('position');
9508             var o = this.getXY();
9509
9510             var l = parseInt(this.getStyle('left'), 10);
9511             var t = parseInt(this.getStyle('top'), 10);
9512
9513             if(isNaN(l)){
9514                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9515             }
9516             if(isNaN(t)){
9517                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9518             }
9519
9520             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9521         },
9522
9523         /**
9524          * Returns the current scroll position of the element.
9525          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9526          */
9527         getScroll : function(){
9528             var d = this.dom, doc = document;
9529             if(d == doc || d == doc.body){
9530                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9531                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9532                 return {left: l, top: t};
9533             }else{
9534                 return {left: d.scrollLeft, top: d.scrollTop};
9535             }
9536         },
9537
9538         /**
9539          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9540          * are convert to standard 6 digit hex color.
9541          * @param {String} attr The css attribute
9542          * @param {String} defaultValue The default value to use when a valid color isn't found
9543          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9544          * YUI color anims.
9545          */
9546         getColor : function(attr, defaultValue, prefix){
9547             var v = this.getStyle(attr);
9548             if(!v || v == "transparent" || v == "inherit") {
9549                 return defaultValue;
9550             }
9551             var color = typeof prefix == "undefined" ? "#" : prefix;
9552             if(v.substr(0, 4) == "rgb("){
9553                 var rvs = v.slice(4, v.length -1).split(",");
9554                 for(var i = 0; i < 3; i++){
9555                     var h = parseInt(rvs[i]).toString(16);
9556                     if(h < 16){
9557                         h = "0" + h;
9558                     }
9559                     color += h;
9560                 }
9561             } else {
9562                 if(v.substr(0, 1) == "#"){
9563                     if(v.length == 4) {
9564                         for(var i = 1; i < 4; i++){
9565                             var c = v.charAt(i);
9566                             color +=  c + c;
9567                         }
9568                     }else if(v.length == 7){
9569                         color += v.substr(1);
9570                     }
9571                 }
9572             }
9573             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9574         },
9575
9576         /**
9577          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9578          * gradient background, rounded corners and a 4-way shadow.
9579          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9580          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9581          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9582          * @return {Roo.Element} this
9583          */
9584         boxWrap : function(cls){
9585             cls = cls || 'x-box';
9586             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9587             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9588             return el;
9589         },
9590
9591         /**
9592          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9593          * @param {String} namespace The namespace in which to look for the attribute
9594          * @param {String} name The attribute name
9595          * @return {String} The attribute value
9596          */
9597         getAttributeNS : Roo.isIE ? function(ns, name){
9598             var d = this.dom;
9599             var type = typeof d[ns+":"+name];
9600             if(type != 'undefined' && type != 'unknown'){
9601                 return d[ns+":"+name];
9602             }
9603             return d[name];
9604         } : function(ns, name){
9605             var d = this.dom;
9606             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9607         },
9608         
9609         
9610         /**
9611          * Sets or Returns the value the dom attribute value
9612          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9613          * @param {String} value (optional) The value to set the attribute to
9614          * @return {String} The attribute value
9615          */
9616         attr : function(name){
9617             if (arguments.length > 1) {
9618                 this.dom.setAttribute(name, arguments[1]);
9619                 return arguments[1];
9620             }
9621             if (typeof(name) == 'object') {
9622                 for(var i in name) {
9623                     this.attr(i, name[i]);
9624                 }
9625                 return name;
9626             }
9627             
9628             
9629             if (!this.dom.hasAttribute(name)) {
9630                 return undefined;
9631             }
9632             return this.dom.getAttribute(name);
9633         }
9634         
9635         
9636         
9637     };
9638
9639     var ep = El.prototype;
9640
9641     /**
9642      * Appends an event handler (Shorthand for addListener)
9643      * @param {String}   eventName     The type of event to append
9644      * @param {Function} fn        The method the event invokes
9645      * @param {Object} scope       (optional) The scope (this object) of the fn
9646      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9647      * @method
9648      */
9649     ep.on = ep.addListener;
9650         // backwards compat
9651     ep.mon = ep.addListener;
9652
9653     /**
9654      * Removes an event handler from this element (shorthand for removeListener)
9655      * @param {String} eventName the type of event to remove
9656      * @param {Function} fn the method the event invokes
9657      * @return {Roo.Element} this
9658      * @method
9659      */
9660     ep.un = ep.removeListener;
9661
9662     /**
9663      * true to automatically adjust width and height settings for box-model issues (default to true)
9664      */
9665     ep.autoBoxAdjust = true;
9666
9667     // private
9668     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9669
9670     // private
9671     El.addUnits = function(v, defaultUnit){
9672         if(v === "" || v == "auto"){
9673             return v;
9674         }
9675         if(v === undefined){
9676             return '';
9677         }
9678         if(typeof v == "number" || !El.unitPattern.test(v)){
9679             return v + (defaultUnit || 'px');
9680         }
9681         return v;
9682     };
9683
9684     // special markup used throughout Roo when box wrapping elements
9685     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9686     /**
9687      * Visibility mode constant - Use visibility to hide element
9688      * @static
9689      * @type Number
9690      */
9691     El.VISIBILITY = 1;
9692     /**
9693      * Visibility mode constant - Use display to hide element
9694      * @static
9695      * @type Number
9696      */
9697     El.DISPLAY = 2;
9698
9699     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9700     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9701     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9702
9703
9704
9705     /**
9706      * @private
9707      */
9708     El.cache = {};
9709
9710     var docEl;
9711
9712     /**
9713      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9714      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9715      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9716      * @return {Element} The Element object
9717      * @static
9718      */
9719     El.get = function(el){
9720         var ex, elm, id;
9721         if(!el){ return null; }
9722         if(typeof el == "string"){ // element id
9723             if(!(elm = document.getElementById(el))){
9724                 return null;
9725             }
9726             if(ex = El.cache[el]){
9727                 ex.dom = elm;
9728             }else{
9729                 ex = El.cache[el] = new El(elm);
9730             }
9731             return ex;
9732         }else if(el.tagName){ // dom element
9733             if(!(id = el.id)){
9734                 id = Roo.id(el);
9735             }
9736             if(ex = El.cache[id]){
9737                 ex.dom = el;
9738             }else{
9739                 ex = El.cache[id] = new El(el);
9740             }
9741             return ex;
9742         }else if(el instanceof El){
9743             if(el != docEl){
9744                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9745                                                               // catch case where it hasn't been appended
9746                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9747             }
9748             return el;
9749         }else if(el.isComposite){
9750             return el;
9751         }else if(el instanceof Array){
9752             return El.select(el);
9753         }else if(el == document){
9754             // create a bogus element object representing the document object
9755             if(!docEl){
9756                 var f = function(){};
9757                 f.prototype = El.prototype;
9758                 docEl = new f();
9759                 docEl.dom = document;
9760             }
9761             return docEl;
9762         }
9763         return null;
9764     };
9765
9766     // private
9767     El.uncache = function(el){
9768         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9769             if(a[i]){
9770                 delete El.cache[a[i].id || a[i]];
9771             }
9772         }
9773     };
9774
9775     // private
9776     // Garbage collection - uncache elements/purge listeners on orphaned elements
9777     // so we don't hold a reference and cause the browser to retain them
9778     El.garbageCollect = function(){
9779         if(!Roo.enableGarbageCollector){
9780             clearInterval(El.collectorThread);
9781             return;
9782         }
9783         for(var eid in El.cache){
9784             var el = El.cache[eid], d = el.dom;
9785             // -------------------------------------------------------
9786             // Determining what is garbage:
9787             // -------------------------------------------------------
9788             // !d
9789             // dom node is null, definitely garbage
9790             // -------------------------------------------------------
9791             // !d.parentNode
9792             // no parentNode == direct orphan, definitely garbage
9793             // -------------------------------------------------------
9794             // !d.offsetParent && !document.getElementById(eid)
9795             // display none elements have no offsetParent so we will
9796             // also try to look it up by it's id. However, check
9797             // offsetParent first so we don't do unneeded lookups.
9798             // This enables collection of elements that are not orphans
9799             // directly, but somewhere up the line they have an orphan
9800             // parent.
9801             // -------------------------------------------------------
9802             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9803                 delete El.cache[eid];
9804                 if(d && Roo.enableListenerCollection){
9805                     E.purgeElement(d);
9806                 }
9807             }
9808         }
9809     }
9810     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9811
9812
9813     // dom is optional
9814     El.Flyweight = function(dom){
9815         this.dom = dom;
9816     };
9817     El.Flyweight.prototype = El.prototype;
9818
9819     El._flyweights = {};
9820     /**
9821      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9822      * the dom node can be overwritten by other code.
9823      * @param {String/HTMLElement} el The dom node or id
9824      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9825      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9826      * @static
9827      * @return {Element} The shared Element object
9828      */
9829     El.fly = function(el, named){
9830         named = named || '_global';
9831         el = Roo.getDom(el);
9832         if(!el){
9833             return null;
9834         }
9835         if(!El._flyweights[named]){
9836             El._flyweights[named] = new El.Flyweight();
9837         }
9838         El._flyweights[named].dom = el;
9839         return El._flyweights[named];
9840     };
9841
9842     /**
9843      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9844      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9845      * Shorthand of {@link Roo.Element#get}
9846      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9847      * @return {Element} The Element object
9848      * @member Roo
9849      * @method get
9850      */
9851     Roo.get = El.get;
9852     /**
9853      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9854      * the dom node can be overwritten by other code.
9855      * Shorthand of {@link Roo.Element#fly}
9856      * @param {String/HTMLElement} el The dom node or id
9857      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9858      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9859      * @static
9860      * @return {Element} The shared Element object
9861      * @member Roo
9862      * @method fly
9863      */
9864     Roo.fly = El.fly;
9865
9866     // speedy lookup for elements never to box adjust
9867     var noBoxAdjust = Roo.isStrict ? {
9868         select:1
9869     } : {
9870         input:1, select:1, textarea:1
9871     };
9872     if(Roo.isIE || Roo.isGecko){
9873         noBoxAdjust['button'] = 1;
9874     }
9875
9876
9877     Roo.EventManager.on(window, 'unload', function(){
9878         delete El.cache;
9879         delete El._flyweights;
9880     });
9881 })();
9882
9883
9884
9885
9886 if(Roo.DomQuery){
9887     Roo.Element.selectorFunction = Roo.DomQuery.select;
9888 }
9889
9890 Roo.Element.select = function(selector, unique, root){
9891     var els;
9892     if(typeof selector == "string"){
9893         els = Roo.Element.selectorFunction(selector, root);
9894     }else if(selector.length !== undefined){
9895         els = selector;
9896     }else{
9897         throw "Invalid selector";
9898     }
9899     if(unique === true){
9900         return new Roo.CompositeElement(els);
9901     }else{
9902         return new Roo.CompositeElementLite(els);
9903     }
9904 };
9905 /**
9906  * Selects elements based on the passed CSS selector to enable working on them as 1.
9907  * @param {String/Array} selector The CSS selector or an array of elements
9908  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9909  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9910  * @return {CompositeElementLite/CompositeElement}
9911  * @member Roo
9912  * @method select
9913  */
9914 Roo.select = Roo.Element.select;
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929 /*
9930  * Based on:
9931  * Ext JS Library 1.1.1
9932  * Copyright(c) 2006-2007, Ext JS, LLC.
9933  *
9934  * Originally Released Under LGPL - original licence link has changed is not relivant.
9935  *
9936  * Fork - LGPL
9937  * <script type="text/javascript">
9938  */
9939
9940
9941
9942 //Notifies Element that fx methods are available
9943 Roo.enableFx = true;
9944
9945 /**
9946  * @class Roo.Fx
9947  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9948  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9949  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9950  * Element effects to work.</p><br/>
9951  *
9952  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9953  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9954  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9955  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9956  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9957  * expected results and should be done with care.</p><br/>
9958  *
9959  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9960  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9961 <pre>
9962 Value  Description
9963 -----  -----------------------------
9964 tl     The top left corner
9965 t      The center of the top edge
9966 tr     The top right corner
9967 l      The center of the left edge
9968 r      The center of the right edge
9969 bl     The bottom left corner
9970 b      The center of the bottom edge
9971 br     The bottom right corner
9972 </pre>
9973  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9974  * below are common options that can be passed to any Fx method.</b>
9975  * @cfg {Function} callback A function called when the effect is finished
9976  * @cfg {Object} scope The scope of the effect function
9977  * @cfg {String} easing A valid Easing value for the effect
9978  * @cfg {String} afterCls A css class to apply after the effect
9979  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9980  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9981  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9982  * effects that end with the element being visually hidden, ignored otherwise)
9983  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9984  * a function which returns such a specification that will be applied to the Element after the effect finishes
9985  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9986  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9987  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9988  */
9989 Roo.Fx = {
9990         /**
9991          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9992          * origin for the slide effect.  This function automatically handles wrapping the element with
9993          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9994          * Usage:
9995          *<pre><code>
9996 // default: slide the element in from the top
9997 el.slideIn();
9998
9999 // custom: slide the element in from the right with a 2-second duration
10000 el.slideIn('r', { duration: 2 });
10001
10002 // common config options shown with default values
10003 el.slideIn('t', {
10004     easing: 'easeOut',
10005     duration: .5
10006 });
10007 </code></pre>
10008          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10009          * @param {Object} options (optional) Object literal with any of the Fx config options
10010          * @return {Roo.Element} The Element
10011          */
10012     slideIn : function(anchor, o){
10013         var el = this.getFxEl();
10014         o = o || {};
10015
10016         el.queueFx(o, function(){
10017
10018             anchor = anchor || "t";
10019
10020             // fix display to visibility
10021             this.fixDisplay();
10022
10023             // restore values after effect
10024             var r = this.getFxRestore();
10025             var b = this.getBox();
10026             // fixed size for slide
10027             this.setSize(b);
10028
10029             // wrap if needed
10030             var wrap = this.fxWrap(r.pos, o, "hidden");
10031
10032             var st = this.dom.style;
10033             st.visibility = "visible";
10034             st.position = "absolute";
10035
10036             // clear out temp styles after slide and unwrap
10037             var after = function(){
10038                 el.fxUnwrap(wrap, r.pos, o);
10039                 st.width = r.width;
10040                 st.height = r.height;
10041                 el.afterFx(o);
10042             };
10043             // time to calc the positions
10044             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10045
10046             switch(anchor.toLowerCase()){
10047                 case "t":
10048                     wrap.setSize(b.width, 0);
10049                     st.left = st.bottom = "0";
10050                     a = {height: bh};
10051                 break;
10052                 case "l":
10053                     wrap.setSize(0, b.height);
10054                     st.right = st.top = "0";
10055                     a = {width: bw};
10056                 break;
10057                 case "r":
10058                     wrap.setSize(0, b.height);
10059                     wrap.setX(b.right);
10060                     st.left = st.top = "0";
10061                     a = {width: bw, points: pt};
10062                 break;
10063                 case "b":
10064                     wrap.setSize(b.width, 0);
10065                     wrap.setY(b.bottom);
10066                     st.left = st.top = "0";
10067                     a = {height: bh, points: pt};
10068                 break;
10069                 case "tl":
10070                     wrap.setSize(0, 0);
10071                     st.right = st.bottom = "0";
10072                     a = {width: bw, height: bh};
10073                 break;
10074                 case "bl":
10075                     wrap.setSize(0, 0);
10076                     wrap.setY(b.y+b.height);
10077                     st.right = st.top = "0";
10078                     a = {width: bw, height: bh, points: pt};
10079                 break;
10080                 case "br":
10081                     wrap.setSize(0, 0);
10082                     wrap.setXY([b.right, b.bottom]);
10083                     st.left = st.top = "0";
10084                     a = {width: bw, height: bh, points: pt};
10085                 break;
10086                 case "tr":
10087                     wrap.setSize(0, 0);
10088                     wrap.setX(b.x+b.width);
10089                     st.left = st.bottom = "0";
10090                     a = {width: bw, height: bh, points: pt};
10091                 break;
10092             }
10093             this.dom.style.visibility = "visible";
10094             wrap.show();
10095
10096             arguments.callee.anim = wrap.fxanim(a,
10097                 o,
10098                 'motion',
10099                 .5,
10100                 'easeOut', after);
10101         });
10102         return this;
10103     },
10104     
10105         /**
10106          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10107          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10108          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10109          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10110          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10111          * Usage:
10112          *<pre><code>
10113 // default: slide the element out to the top
10114 el.slideOut();
10115
10116 // custom: slide the element out to the right with a 2-second duration
10117 el.slideOut('r', { duration: 2 });
10118
10119 // common config options shown with default values
10120 el.slideOut('t', {
10121     easing: 'easeOut',
10122     duration: .5,
10123     remove: false,
10124     useDisplay: false
10125 });
10126 </code></pre>
10127          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10128          * @param {Object} options (optional) Object literal with any of the Fx config options
10129          * @return {Roo.Element} The Element
10130          */
10131     slideOut : function(anchor, o){
10132         var el = this.getFxEl();
10133         o = o || {};
10134
10135         el.queueFx(o, function(){
10136
10137             anchor = anchor || "t";
10138
10139             // restore values after effect
10140             var r = this.getFxRestore();
10141             
10142             var b = this.getBox();
10143             // fixed size for slide
10144             this.setSize(b);
10145
10146             // wrap if needed
10147             var wrap = this.fxWrap(r.pos, o, "visible");
10148
10149             var st = this.dom.style;
10150             st.visibility = "visible";
10151             st.position = "absolute";
10152
10153             wrap.setSize(b);
10154
10155             var after = function(){
10156                 if(o.useDisplay){
10157                     el.setDisplayed(false);
10158                 }else{
10159                     el.hide();
10160                 }
10161
10162                 el.fxUnwrap(wrap, r.pos, o);
10163
10164                 st.width = r.width;
10165                 st.height = r.height;
10166
10167                 el.afterFx(o);
10168             };
10169
10170             var a, zero = {to: 0};
10171             switch(anchor.toLowerCase()){
10172                 case "t":
10173                     st.left = st.bottom = "0";
10174                     a = {height: zero};
10175                 break;
10176                 case "l":
10177                     st.right = st.top = "0";
10178                     a = {width: zero};
10179                 break;
10180                 case "r":
10181                     st.left = st.top = "0";
10182                     a = {width: zero, points: {to:[b.right, b.y]}};
10183                 break;
10184                 case "b":
10185                     st.left = st.top = "0";
10186                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10187                 break;
10188                 case "tl":
10189                     st.right = st.bottom = "0";
10190                     a = {width: zero, height: zero};
10191                 break;
10192                 case "bl":
10193                     st.right = st.top = "0";
10194                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10195                 break;
10196                 case "br":
10197                     st.left = st.top = "0";
10198                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10199                 break;
10200                 case "tr":
10201                     st.left = st.bottom = "0";
10202                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10203                 break;
10204             }
10205
10206             arguments.callee.anim = wrap.fxanim(a,
10207                 o,
10208                 'motion',
10209                 .5,
10210                 "easeOut", after);
10211         });
10212         return this;
10213     },
10214
10215         /**
10216          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10217          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10218          * The element must be removed from the DOM using the 'remove' config option if desired.
10219          * Usage:
10220          *<pre><code>
10221 // default
10222 el.puff();
10223
10224 // common config options shown with default values
10225 el.puff({
10226     easing: 'easeOut',
10227     duration: .5,
10228     remove: false,
10229     useDisplay: false
10230 });
10231 </code></pre>
10232          * @param {Object} options (optional) Object literal with any of the Fx config options
10233          * @return {Roo.Element} The Element
10234          */
10235     puff : function(o){
10236         var el = this.getFxEl();
10237         o = o || {};
10238
10239         el.queueFx(o, function(){
10240             this.clearOpacity();
10241             this.show();
10242
10243             // restore values after effect
10244             var r = this.getFxRestore();
10245             var st = this.dom.style;
10246
10247             var after = function(){
10248                 if(o.useDisplay){
10249                     el.setDisplayed(false);
10250                 }else{
10251                     el.hide();
10252                 }
10253
10254                 el.clearOpacity();
10255
10256                 el.setPositioning(r.pos);
10257                 st.width = r.width;
10258                 st.height = r.height;
10259                 st.fontSize = '';
10260                 el.afterFx(o);
10261             };
10262
10263             var width = this.getWidth();
10264             var height = this.getHeight();
10265
10266             arguments.callee.anim = this.fxanim({
10267                     width : {to: this.adjustWidth(width * 2)},
10268                     height : {to: this.adjustHeight(height * 2)},
10269                     points : {by: [-(width * .5), -(height * .5)]},
10270                     opacity : {to: 0},
10271                     fontSize: {to:200, unit: "%"}
10272                 },
10273                 o,
10274                 'motion',
10275                 .5,
10276                 "easeOut", after);
10277         });
10278         return this;
10279     },
10280
10281         /**
10282          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10283          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10284          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10285          * Usage:
10286          *<pre><code>
10287 // default
10288 el.switchOff();
10289
10290 // all config options shown with default values
10291 el.switchOff({
10292     easing: 'easeIn',
10293     duration: .3,
10294     remove: false,
10295     useDisplay: false
10296 });
10297 </code></pre>
10298          * @param {Object} options (optional) Object literal with any of the Fx config options
10299          * @return {Roo.Element} The Element
10300          */
10301     switchOff : function(o){
10302         var el = this.getFxEl();
10303         o = o || {};
10304
10305         el.queueFx(o, function(){
10306             this.clearOpacity();
10307             this.clip();
10308
10309             // restore values after effect
10310             var r = this.getFxRestore();
10311             var st = this.dom.style;
10312
10313             var after = function(){
10314                 if(o.useDisplay){
10315                     el.setDisplayed(false);
10316                 }else{
10317                     el.hide();
10318                 }
10319
10320                 el.clearOpacity();
10321                 el.setPositioning(r.pos);
10322                 st.width = r.width;
10323                 st.height = r.height;
10324
10325                 el.afterFx(o);
10326             };
10327
10328             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10329                 this.clearOpacity();
10330                 (function(){
10331                     this.fxanim({
10332                         height:{to:1},
10333                         points:{by:[0, this.getHeight() * .5]}
10334                     }, o, 'motion', 0.3, 'easeIn', after);
10335                 }).defer(100, this);
10336             });
10337         });
10338         return this;
10339     },
10340
10341     /**
10342      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10343      * changed using the "attr" config option) and then fading back to the original color. If no original
10344      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10345      * Usage:
10346 <pre><code>
10347 // default: highlight background to yellow
10348 el.highlight();
10349
10350 // custom: highlight foreground text to blue for 2 seconds
10351 el.highlight("0000ff", { attr: 'color', duration: 2 });
10352
10353 // common config options shown with default values
10354 el.highlight("ffff9c", {
10355     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10356     endColor: (current color) or "ffffff",
10357     easing: 'easeIn',
10358     duration: 1
10359 });
10360 </code></pre>
10361      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10362      * @param {Object} options (optional) Object literal with any of the Fx config options
10363      * @return {Roo.Element} The Element
10364      */ 
10365     highlight : function(color, o){
10366         var el = this.getFxEl();
10367         o = o || {};
10368
10369         el.queueFx(o, function(){
10370             color = color || "ffff9c";
10371             attr = o.attr || "backgroundColor";
10372
10373             this.clearOpacity();
10374             this.show();
10375
10376             var origColor = this.getColor(attr);
10377             var restoreColor = this.dom.style[attr];
10378             endColor = (o.endColor || origColor) || "ffffff";
10379
10380             var after = function(){
10381                 el.dom.style[attr] = restoreColor;
10382                 el.afterFx(o);
10383             };
10384
10385             var a = {};
10386             a[attr] = {from: color, to: endColor};
10387             arguments.callee.anim = this.fxanim(a,
10388                 o,
10389                 'color',
10390                 1,
10391                 'easeIn', after);
10392         });
10393         return this;
10394     },
10395
10396    /**
10397     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10398     * Usage:
10399 <pre><code>
10400 // default: a single light blue ripple
10401 el.frame();
10402
10403 // custom: 3 red ripples lasting 3 seconds total
10404 el.frame("ff0000", 3, { duration: 3 });
10405
10406 // common config options shown with default values
10407 el.frame("C3DAF9", 1, {
10408     duration: 1 //duration of entire animation (not each individual ripple)
10409     // Note: Easing is not configurable and will be ignored if included
10410 });
10411 </code></pre>
10412     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10413     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10414     * @param {Object} options (optional) Object literal with any of the Fx config options
10415     * @return {Roo.Element} The Element
10416     */
10417     frame : function(color, count, o){
10418         var el = this.getFxEl();
10419         o = o || {};
10420
10421         el.queueFx(o, function(){
10422             color = color || "#C3DAF9";
10423             if(color.length == 6){
10424                 color = "#" + color;
10425             }
10426             count = count || 1;
10427             duration = o.duration || 1;
10428             this.show();
10429
10430             var b = this.getBox();
10431             var animFn = function(){
10432                 var proxy = this.createProxy({
10433
10434                      style:{
10435                         visbility:"hidden",
10436                         position:"absolute",
10437                         "z-index":"35000", // yee haw
10438                         border:"0px solid " + color
10439                      }
10440                   });
10441                 var scale = Roo.isBorderBox ? 2 : 1;
10442                 proxy.animate({
10443                     top:{from:b.y, to:b.y - 20},
10444                     left:{from:b.x, to:b.x - 20},
10445                     borderWidth:{from:0, to:10},
10446                     opacity:{from:1, to:0},
10447                     height:{from:b.height, to:(b.height + (20*scale))},
10448                     width:{from:b.width, to:(b.width + (20*scale))}
10449                 }, duration, function(){
10450                     proxy.remove();
10451                 });
10452                 if(--count > 0){
10453                      animFn.defer((duration/2)*1000, this);
10454                 }else{
10455                     el.afterFx(o);
10456                 }
10457             };
10458             animFn.call(this);
10459         });
10460         return this;
10461     },
10462
10463    /**
10464     * Creates a pause before any subsequent queued effects begin.  If there are
10465     * no effects queued after the pause it will have no effect.
10466     * Usage:
10467 <pre><code>
10468 el.pause(1);
10469 </code></pre>
10470     * @param {Number} seconds The length of time to pause (in seconds)
10471     * @return {Roo.Element} The Element
10472     */
10473     pause : function(seconds){
10474         var el = this.getFxEl();
10475         var o = {};
10476
10477         el.queueFx(o, function(){
10478             setTimeout(function(){
10479                 el.afterFx(o);
10480             }, seconds * 1000);
10481         });
10482         return this;
10483     },
10484
10485    /**
10486     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10487     * using the "endOpacity" config option.
10488     * Usage:
10489 <pre><code>
10490 // default: fade in from opacity 0 to 100%
10491 el.fadeIn();
10492
10493 // custom: fade in from opacity 0 to 75% over 2 seconds
10494 el.fadeIn({ endOpacity: .75, duration: 2});
10495
10496 // common config options shown with default values
10497 el.fadeIn({
10498     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10499     easing: 'easeOut',
10500     duration: .5
10501 });
10502 </code></pre>
10503     * @param {Object} options (optional) Object literal with any of the Fx config options
10504     * @return {Roo.Element} The Element
10505     */
10506     fadeIn : function(o){
10507         var el = this.getFxEl();
10508         o = o || {};
10509         el.queueFx(o, function(){
10510             this.setOpacity(0);
10511             this.fixDisplay();
10512             this.dom.style.visibility = 'visible';
10513             var to = o.endOpacity || 1;
10514             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10515                 o, null, .5, "easeOut", function(){
10516                 if(to == 1){
10517                     this.clearOpacity();
10518                 }
10519                 el.afterFx(o);
10520             });
10521         });
10522         return this;
10523     },
10524
10525    /**
10526     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10527     * using the "endOpacity" config option.
10528     * Usage:
10529 <pre><code>
10530 // default: fade out from the element's current opacity to 0
10531 el.fadeOut();
10532
10533 // custom: fade out from the element's current opacity to 25% over 2 seconds
10534 el.fadeOut({ endOpacity: .25, duration: 2});
10535
10536 // common config options shown with default values
10537 el.fadeOut({
10538     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10539     easing: 'easeOut',
10540     duration: .5
10541     remove: false,
10542     useDisplay: false
10543 });
10544 </code></pre>
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     fadeOut : function(o){
10549         var el = this.getFxEl();
10550         o = o || {};
10551         el.queueFx(o, function(){
10552             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10553                 o, null, .5, "easeOut", function(){
10554                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10555                      this.dom.style.display = "none";
10556                 }else{
10557                      this.dom.style.visibility = "hidden";
10558                 }
10559                 this.clearOpacity();
10560                 el.afterFx(o);
10561             });
10562         });
10563         return this;
10564     },
10565
10566    /**
10567     * Animates the transition of an element's dimensions from a starting height/width
10568     * to an ending height/width.
10569     * Usage:
10570 <pre><code>
10571 // change height and width to 100x100 pixels
10572 el.scale(100, 100);
10573
10574 // common config options shown with default values.  The height and width will default to
10575 // the element's existing values if passed as null.
10576 el.scale(
10577     [element's width],
10578     [element's height], {
10579     easing: 'easeOut',
10580     duration: .35
10581 });
10582 </code></pre>
10583     * @param {Number} width  The new width (pass undefined to keep the original width)
10584     * @param {Number} height  The new height (pass undefined to keep the original height)
10585     * @param {Object} options (optional) Object literal with any of the Fx config options
10586     * @return {Roo.Element} The Element
10587     */
10588     scale : function(w, h, o){
10589         this.shift(Roo.apply({}, o, {
10590             width: w,
10591             height: h
10592         }));
10593         return this;
10594     },
10595
10596    /**
10597     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10598     * Any of these properties not specified in the config object will not be changed.  This effect 
10599     * requires that at least one new dimension, position or opacity setting must be passed in on
10600     * the config object in order for the function to have any effect.
10601     * Usage:
10602 <pre><code>
10603 // slide the element horizontally to x position 200 while changing the height and opacity
10604 el.shift({ x: 200, height: 50, opacity: .8 });
10605
10606 // common config options shown with default values.
10607 el.shift({
10608     width: [element's width],
10609     height: [element's height],
10610     x: [element's x position],
10611     y: [element's y position],
10612     opacity: [element's opacity],
10613     easing: 'easeOut',
10614     duration: .35
10615 });
10616 </code></pre>
10617     * @param {Object} options  Object literal with any of the Fx config options
10618     * @return {Roo.Element} The Element
10619     */
10620     shift : function(o){
10621         var el = this.getFxEl();
10622         o = o || {};
10623         el.queueFx(o, function(){
10624             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10625             if(w !== undefined){
10626                 a.width = {to: this.adjustWidth(w)};
10627             }
10628             if(h !== undefined){
10629                 a.height = {to: this.adjustHeight(h)};
10630             }
10631             if(x !== undefined || y !== undefined){
10632                 a.points = {to: [
10633                     x !== undefined ? x : this.getX(),
10634                     y !== undefined ? y : this.getY()
10635                 ]};
10636             }
10637             if(op !== undefined){
10638                 a.opacity = {to: op};
10639             }
10640             if(o.xy !== undefined){
10641                 a.points = {to: o.xy};
10642             }
10643             arguments.callee.anim = this.fxanim(a,
10644                 o, 'motion', .35, "easeOut", function(){
10645                 el.afterFx(o);
10646             });
10647         });
10648         return this;
10649     },
10650
10651         /**
10652          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10653          * ending point of the effect.
10654          * Usage:
10655          *<pre><code>
10656 // default: slide the element downward while fading out
10657 el.ghost();
10658
10659 // custom: slide the element out to the right with a 2-second duration
10660 el.ghost('r', { duration: 2 });
10661
10662 // common config options shown with default values
10663 el.ghost('b', {
10664     easing: 'easeOut',
10665     duration: .5
10666     remove: false,
10667     useDisplay: false
10668 });
10669 </code></pre>
10670          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10671          * @param {Object} options (optional) Object literal with any of the Fx config options
10672          * @return {Roo.Element} The Element
10673          */
10674     ghost : function(anchor, o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677
10678         el.queueFx(o, function(){
10679             anchor = anchor || "b";
10680
10681             // restore values after effect
10682             var r = this.getFxRestore();
10683             var w = this.getWidth(),
10684                 h = this.getHeight();
10685
10686             var st = this.dom.style;
10687
10688             var after = function(){
10689                 if(o.useDisplay){
10690                     el.setDisplayed(false);
10691                 }else{
10692                     el.hide();
10693                 }
10694
10695                 el.clearOpacity();
10696                 el.setPositioning(r.pos);
10697                 st.width = r.width;
10698                 st.height = r.height;
10699
10700                 el.afterFx(o);
10701             };
10702
10703             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10704             switch(anchor.toLowerCase()){
10705                 case "t":
10706                     pt.by = [0, -h];
10707                 break;
10708                 case "l":
10709                     pt.by = [-w, 0];
10710                 break;
10711                 case "r":
10712                     pt.by = [w, 0];
10713                 break;
10714                 case "b":
10715                     pt.by = [0, h];
10716                 break;
10717                 case "tl":
10718                     pt.by = [-w, -h];
10719                 break;
10720                 case "bl":
10721                     pt.by = [-w, h];
10722                 break;
10723                 case "br":
10724                     pt.by = [w, h];
10725                 break;
10726                 case "tr":
10727                     pt.by = [w, -h];
10728                 break;
10729             }
10730
10731             arguments.callee.anim = this.fxanim(a,
10732                 o,
10733                 'motion',
10734                 .5,
10735                 "easeOut", after);
10736         });
10737         return this;
10738     },
10739
10740         /**
10741          * Ensures that all effects queued after syncFx is called on the element are
10742          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10743          * @return {Roo.Element} The Element
10744          */
10745     syncFx : function(){
10746         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10747             block : false,
10748             concurrent : true,
10749             stopFx : false
10750         });
10751         return this;
10752     },
10753
10754         /**
10755          * Ensures that all effects queued after sequenceFx is called on the element are
10756          * run in sequence.  This is the opposite of {@link #syncFx}.
10757          * @return {Roo.Element} The Element
10758          */
10759     sequenceFx : function(){
10760         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10761             block : false,
10762             concurrent : false,
10763             stopFx : false
10764         });
10765         return this;
10766     },
10767
10768         /* @private */
10769     nextFx : function(){
10770         var ef = this.fxQueue[0];
10771         if(ef){
10772             ef.call(this);
10773         }
10774     },
10775
10776         /**
10777          * Returns true if the element has any effects actively running or queued, else returns false.
10778          * @return {Boolean} True if element has active effects, else false
10779          */
10780     hasActiveFx : function(){
10781         return this.fxQueue && this.fxQueue[0];
10782     },
10783
10784         /**
10785          * Stops any running effects and clears the element's internal effects queue if it contains
10786          * any additional effects that haven't started yet.
10787          * @return {Roo.Element} The Element
10788          */
10789     stopFx : function(){
10790         if(this.hasActiveFx()){
10791             var cur = this.fxQueue[0];
10792             if(cur && cur.anim && cur.anim.isAnimated()){
10793                 this.fxQueue = [cur]; // clear out others
10794                 cur.anim.stop(true);
10795             }
10796         }
10797         return this;
10798     },
10799
10800         /* @private */
10801     beforeFx : function(o){
10802         if(this.hasActiveFx() && !o.concurrent){
10803            if(o.stopFx){
10804                this.stopFx();
10805                return true;
10806            }
10807            return false;
10808         }
10809         return true;
10810     },
10811
10812         /**
10813          * Returns true if the element is currently blocking so that no other effect can be queued
10814          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10815          * used to ensure that an effect initiated by a user action runs to completion prior to the
10816          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10817          * @return {Boolean} True if blocking, else false
10818          */
10819     hasFxBlock : function(){
10820         var q = this.fxQueue;
10821         return q && q[0] && q[0].block;
10822     },
10823
10824         /* @private */
10825     queueFx : function(o, fn){
10826         if(!this.fxQueue){
10827             this.fxQueue = [];
10828         }
10829         if(!this.hasFxBlock()){
10830             Roo.applyIf(o, this.fxDefaults);
10831             if(!o.concurrent){
10832                 var run = this.beforeFx(o);
10833                 fn.block = o.block;
10834                 this.fxQueue.push(fn);
10835                 if(run){
10836                     this.nextFx();
10837                 }
10838             }else{
10839                 fn.call(this);
10840             }
10841         }
10842         return this;
10843     },
10844
10845         /* @private */
10846     fxWrap : function(pos, o, vis){
10847         var wrap;
10848         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10849             var wrapXY;
10850             if(o.fixPosition){
10851                 wrapXY = this.getXY();
10852             }
10853             var div = document.createElement("div");
10854             div.style.visibility = vis;
10855             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10856             wrap.setPositioning(pos);
10857             if(wrap.getStyle("position") == "static"){
10858                 wrap.position("relative");
10859             }
10860             this.clearPositioning('auto');
10861             wrap.clip();
10862             wrap.dom.appendChild(this.dom);
10863             if(wrapXY){
10864                 wrap.setXY(wrapXY);
10865             }
10866         }
10867         return wrap;
10868     },
10869
10870         /* @private */
10871     fxUnwrap : function(wrap, pos, o){
10872         this.clearPositioning();
10873         this.setPositioning(pos);
10874         if(!o.wrap){
10875             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10876             wrap.remove();
10877         }
10878     },
10879
10880         /* @private */
10881     getFxRestore : function(){
10882         var st = this.dom.style;
10883         return {pos: this.getPositioning(), width: st.width, height : st.height};
10884     },
10885
10886         /* @private */
10887     afterFx : function(o){
10888         if(o.afterStyle){
10889             this.applyStyles(o.afterStyle);
10890         }
10891         if(o.afterCls){
10892             this.addClass(o.afterCls);
10893         }
10894         if(o.remove === true){
10895             this.remove();
10896         }
10897         Roo.callback(o.callback, o.scope, [this]);
10898         if(!o.concurrent){
10899             this.fxQueue.shift();
10900             this.nextFx();
10901         }
10902     },
10903
10904         /* @private */
10905     getFxEl : function(){ // support for composite element fx
10906         return Roo.get(this.dom);
10907     },
10908
10909         /* @private */
10910     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10911         animType = animType || 'run';
10912         opt = opt || {};
10913         var anim = Roo.lib.Anim[animType](
10914             this.dom, args,
10915             (opt.duration || defaultDur) || .35,
10916             (opt.easing || defaultEase) || 'easeOut',
10917             function(){
10918                 Roo.callback(cb, this);
10919             },
10920             this
10921         );
10922         opt.anim = anim;
10923         return anim;
10924     }
10925 };
10926
10927 // backwords compat
10928 Roo.Fx.resize = Roo.Fx.scale;
10929
10930 //When included, Roo.Fx is automatically applied to Element so that all basic
10931 //effects are available directly via the Element API
10932 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10933  * Based on:
10934  * Ext JS Library 1.1.1
10935  * Copyright(c) 2006-2007, Ext JS, LLC.
10936  *
10937  * Originally Released Under LGPL - original licence link has changed is not relivant.
10938  *
10939  * Fork - LGPL
10940  * <script type="text/javascript">
10941  */
10942
10943
10944 /**
10945  * @class Roo.CompositeElement
10946  * Standard composite class. Creates a Roo.Element for every element in the collection.
10947  * <br><br>
10948  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10949  * actions will be performed on all the elements in this collection.</b>
10950  * <br><br>
10951  * All methods return <i>this</i> and can be chained.
10952  <pre><code>
10953  var els = Roo.select("#some-el div.some-class", true);
10954  // or select directly from an existing element
10955  var el = Roo.get('some-el');
10956  el.select('div.some-class', true);
10957
10958  els.setWidth(100); // all elements become 100 width
10959  els.hide(true); // all elements fade out and hide
10960  // or
10961  els.setWidth(100).hide(true);
10962  </code></pre>
10963  */
10964 Roo.CompositeElement = function(els){
10965     this.elements = [];
10966     this.addElements(els);
10967 };
10968 Roo.CompositeElement.prototype = {
10969     isComposite: true,
10970     addElements : function(els){
10971         if(!els) return this;
10972         if(typeof els == "string"){
10973             els = Roo.Element.selectorFunction(els);
10974         }
10975         var yels = this.elements;
10976         var index = yels.length-1;
10977         for(var i = 0, len = els.length; i < len; i++) {
10978                 yels[++index] = Roo.get(els[i]);
10979         }
10980         return this;
10981     },
10982
10983     /**
10984     * Clears this composite and adds the elements returned by the passed selector.
10985     * @param {String/Array} els A string CSS selector, an array of elements or an element
10986     * @return {CompositeElement} this
10987     */
10988     fill : function(els){
10989         this.elements = [];
10990         this.add(els);
10991         return this;
10992     },
10993
10994     /**
10995     * Filters this composite to only elements that match the passed selector.
10996     * @param {String} selector A string CSS selector
10997     * @param {Boolean} inverse return inverse filter (not matches)
10998     * @return {CompositeElement} this
10999     */
11000     filter : function(selector, inverse){
11001         var els = [];
11002         inverse = inverse || false;
11003         this.each(function(el){
11004             var match = inverse ? !el.is(selector) : el.is(selector);
11005             if(match){
11006                 els[els.length] = el.dom;
11007             }
11008         });
11009         this.fill(els);
11010         return this;
11011     },
11012
11013     invoke : function(fn, args){
11014         var els = this.elements;
11015         for(var i = 0, len = els.length; i < len; i++) {
11016                 Roo.Element.prototype[fn].apply(els[i], args);
11017         }
11018         return this;
11019     },
11020     /**
11021     * Adds elements to this composite.
11022     * @param {String/Array} els A string CSS selector, an array of elements or an element
11023     * @return {CompositeElement} this
11024     */
11025     add : function(els){
11026         if(typeof els == "string"){
11027             this.addElements(Roo.Element.selectorFunction(els));
11028         }else if(els.length !== undefined){
11029             this.addElements(els);
11030         }else{
11031             this.addElements([els]);
11032         }
11033         return this;
11034     },
11035     /**
11036     * Calls the passed function passing (el, this, index) for each element in this composite.
11037     * @param {Function} fn The function to call
11038     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11039     * @return {CompositeElement} this
11040     */
11041     each : function(fn, scope){
11042         var els = this.elements;
11043         for(var i = 0, len = els.length; i < len; i++){
11044             if(fn.call(scope || els[i], els[i], this, i) === false) {
11045                 break;
11046             }
11047         }
11048         return this;
11049     },
11050
11051     /**
11052      * Returns the Element object at the specified index
11053      * @param {Number} index
11054      * @return {Roo.Element}
11055      */
11056     item : function(index){
11057         return this.elements[index] || null;
11058     },
11059
11060     /**
11061      * Returns the first Element
11062      * @return {Roo.Element}
11063      */
11064     first : function(){
11065         return this.item(0);
11066     },
11067
11068     /**
11069      * Returns the last Element
11070      * @return {Roo.Element}
11071      */
11072     last : function(){
11073         return this.item(this.elements.length-1);
11074     },
11075
11076     /**
11077      * Returns the number of elements in this composite
11078      * @return Number
11079      */
11080     getCount : function(){
11081         return this.elements.length;
11082     },
11083
11084     /**
11085      * Returns true if this composite contains the passed element
11086      * @return Boolean
11087      */
11088     contains : function(el){
11089         return this.indexOf(el) !== -1;
11090     },
11091
11092     /**
11093      * Returns true if this composite contains the passed element
11094      * @return Boolean
11095      */
11096     indexOf : function(el){
11097         return this.elements.indexOf(Roo.get(el));
11098     },
11099
11100
11101     /**
11102     * Removes the specified element(s).
11103     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11104     * or an array of any of those.
11105     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11106     * @return {CompositeElement} this
11107     */
11108     removeElement : function(el, removeDom){
11109         if(el instanceof Array){
11110             for(var i = 0, len = el.length; i < len; i++){
11111                 this.removeElement(el[i]);
11112             }
11113             return this;
11114         }
11115         var index = typeof el == 'number' ? el : this.indexOf(el);
11116         if(index !== -1){
11117             if(removeDom){
11118                 var d = this.elements[index];
11119                 if(d.dom){
11120                     d.remove();
11121                 }else{
11122                     d.parentNode.removeChild(d);
11123                 }
11124             }
11125             this.elements.splice(index, 1);
11126         }
11127         return this;
11128     },
11129
11130     /**
11131     * Replaces the specified element with the passed element.
11132     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11133     * to replace.
11134     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11135     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11136     * @return {CompositeElement} this
11137     */
11138     replaceElement : function(el, replacement, domReplace){
11139         var index = typeof el == 'number' ? el : this.indexOf(el);
11140         if(index !== -1){
11141             if(domReplace){
11142                 this.elements[index].replaceWith(replacement);
11143             }else{
11144                 this.elements.splice(index, 1, Roo.get(replacement))
11145             }
11146         }
11147         return this;
11148     },
11149
11150     /**
11151      * Removes all elements.
11152      */
11153     clear : function(){
11154         this.elements = [];
11155     }
11156 };
11157 (function(){
11158     Roo.CompositeElement.createCall = function(proto, fnName){
11159         if(!proto[fnName]){
11160             proto[fnName] = function(){
11161                 return this.invoke(fnName, arguments);
11162             };
11163         }
11164     };
11165     for(var fnName in Roo.Element.prototype){
11166         if(typeof Roo.Element.prototype[fnName] == "function"){
11167             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11168         }
11169     };
11170 })();
11171 /*
11172  * Based on:
11173  * Ext JS Library 1.1.1
11174  * Copyright(c) 2006-2007, Ext JS, LLC.
11175  *
11176  * Originally Released Under LGPL - original licence link has changed is not relivant.
11177  *
11178  * Fork - LGPL
11179  * <script type="text/javascript">
11180  */
11181
11182 /**
11183  * @class Roo.CompositeElementLite
11184  * @extends Roo.CompositeElement
11185  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11186  <pre><code>
11187  var els = Roo.select("#some-el div.some-class");
11188  // or select directly from an existing element
11189  var el = Roo.get('some-el');
11190  el.select('div.some-class');
11191
11192  els.setWidth(100); // all elements become 100 width
11193  els.hide(true); // all elements fade out and hide
11194  // or
11195  els.setWidth(100).hide(true);
11196  </code></pre><br><br>
11197  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11198  * actions will be performed on all the elements in this collection.</b>
11199  */
11200 Roo.CompositeElementLite = function(els){
11201     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11202     this.el = new Roo.Element.Flyweight();
11203 };
11204 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11205     addElements : function(els){
11206         if(els){
11207             if(els instanceof Array){
11208                 this.elements = this.elements.concat(els);
11209             }else{
11210                 var yels = this.elements;
11211                 var index = yels.length-1;
11212                 for(var i = 0, len = els.length; i < len; i++) {
11213                     yels[++index] = els[i];
11214                 }
11215             }
11216         }
11217         return this;
11218     },
11219     invoke : function(fn, args){
11220         var els = this.elements;
11221         var el = this.el;
11222         for(var i = 0, len = els.length; i < len; i++) {
11223             el.dom = els[i];
11224                 Roo.Element.prototype[fn].apply(el, args);
11225         }
11226         return this;
11227     },
11228     /**
11229      * Returns a flyweight Element of the dom element object at the specified index
11230      * @param {Number} index
11231      * @return {Roo.Element}
11232      */
11233     item : function(index){
11234         if(!this.elements[index]){
11235             return null;
11236         }
11237         this.el.dom = this.elements[index];
11238         return this.el;
11239     },
11240
11241     // fixes scope with flyweight
11242     addListener : function(eventName, handler, scope, opt){
11243         var els = this.elements;
11244         for(var i = 0, len = els.length; i < len; i++) {
11245             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11246         }
11247         return this;
11248     },
11249
11250     /**
11251     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11252     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11253     * a reference to the dom node, use el.dom.</b>
11254     * @param {Function} fn The function to call
11255     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11256     * @return {CompositeElement} this
11257     */
11258     each : function(fn, scope){
11259         var els = this.elements;
11260         var el = this.el;
11261         for(var i = 0, len = els.length; i < len; i++){
11262             el.dom = els[i];
11263                 if(fn.call(scope || el, el, this, i) === false){
11264                 break;
11265             }
11266         }
11267         return this;
11268     },
11269
11270     indexOf : function(el){
11271         return this.elements.indexOf(Roo.getDom(el));
11272     },
11273
11274     replaceElement : function(el, replacement, domReplace){
11275         var index = typeof el == 'number' ? el : this.indexOf(el);
11276         if(index !== -1){
11277             replacement = Roo.getDom(replacement);
11278             if(domReplace){
11279                 var d = this.elements[index];
11280                 d.parentNode.insertBefore(replacement, d);
11281                 d.parentNode.removeChild(d);
11282             }
11283             this.elements.splice(index, 1, replacement);
11284         }
11285         return this;
11286     }
11287 });
11288 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11289
11290 /*
11291  * Based on:
11292  * Ext JS Library 1.1.1
11293  * Copyright(c) 2006-2007, Ext JS, LLC.
11294  *
11295  * Originally Released Under LGPL - original licence link has changed is not relivant.
11296  *
11297  * Fork - LGPL
11298  * <script type="text/javascript">
11299  */
11300
11301  
11302
11303 /**
11304  * @class Roo.data.Connection
11305  * @extends Roo.util.Observable
11306  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11307  * either to a configured URL, or to a URL specified at request time.<br><br>
11308  * <p>
11309  * Requests made by this class are asynchronous, and will return immediately. No data from
11310  * the server will be available to the statement immediately following the {@link #request} call.
11311  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11312  * <p>
11313  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11314  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11315  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11316  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11317  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11318  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11319  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11320  * standard DOM methods.
11321  * @constructor
11322  * @param {Object} config a configuration object.
11323  */
11324 Roo.data.Connection = function(config){
11325     Roo.apply(this, config);
11326     this.addEvents({
11327         /**
11328          * @event beforerequest
11329          * Fires before a network request is made to retrieve a data object.
11330          * @param {Connection} conn This Connection object.
11331          * @param {Object} options The options config object passed to the {@link #request} method.
11332          */
11333         "beforerequest" : true,
11334         /**
11335          * @event requestcomplete
11336          * Fires if the request was successfully completed.
11337          * @param {Connection} conn This Connection object.
11338          * @param {Object} response The XHR object containing the response data.
11339          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11340          * @param {Object} options The options config object passed to the {@link #request} method.
11341          */
11342         "requestcomplete" : true,
11343         /**
11344          * @event requestexception
11345          * Fires if an error HTTP status was returned from the server.
11346          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11347          * @param {Connection} conn This Connection object.
11348          * @param {Object} response The XHR object containing the response data.
11349          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11350          * @param {Object} options The options config object passed to the {@link #request} method.
11351          */
11352         "requestexception" : true
11353     });
11354     Roo.data.Connection.superclass.constructor.call(this);
11355 };
11356
11357 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11358     /**
11359      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11360      */
11361     /**
11362      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11363      * extra parameters to each request made by this object. (defaults to undefined)
11364      */
11365     /**
11366      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11367      *  to each request made by this object. (defaults to undefined)
11368      */
11369     /**
11370      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11371      */
11372     /**
11373      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11374      */
11375     timeout : 30000,
11376     /**
11377      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11378      * @type Boolean
11379      */
11380     autoAbort:false,
11381
11382     /**
11383      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11384      * @type Boolean
11385      */
11386     disableCaching: true,
11387
11388     /**
11389      * Sends an HTTP request to a remote server.
11390      * @param {Object} options An object which may contain the following properties:<ul>
11391      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11392      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11393      * request, a url encoded string or a function to call to get either.</li>
11394      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11395      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11396      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11397      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11398      * <li>options {Object} The parameter to the request call.</li>
11399      * <li>success {Boolean} True if the request succeeded.</li>
11400      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11401      * </ul></li>
11402      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11403      * The callback is passed the following parameters:<ul>
11404      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11405      * <li>options {Object} The parameter to the request call.</li>
11406      * </ul></li>
11407      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11408      * The callback is passed the following parameters:<ul>
11409      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11410      * <li>options {Object} The parameter to the request call.</li>
11411      * </ul></li>
11412      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11413      * for the callback function. Defaults to the browser window.</li>
11414      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11415      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11416      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11417      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11418      * params for the post data. Any params will be appended to the URL.</li>
11419      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11420      * </ul>
11421      * @return {Number} transactionId
11422      */
11423     request : function(o){
11424         if(this.fireEvent("beforerequest", this, o) !== false){
11425             var p = o.params;
11426
11427             if(typeof p == "function"){
11428                 p = p.call(o.scope||window, o);
11429             }
11430             if(typeof p == "object"){
11431                 p = Roo.urlEncode(o.params);
11432             }
11433             if(this.extraParams){
11434                 var extras = Roo.urlEncode(this.extraParams);
11435                 p = p ? (p + '&' + extras) : extras;
11436             }
11437
11438             var url = o.url || this.url;
11439             if(typeof url == 'function'){
11440                 url = url.call(o.scope||window, o);
11441             }
11442
11443             if(o.form){
11444                 var form = Roo.getDom(o.form);
11445                 url = url || form.action;
11446
11447                 var enctype = form.getAttribute("enctype");
11448                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11449                     return this.doFormUpload(o, p, url);
11450                 }
11451                 var f = Roo.lib.Ajax.serializeForm(form);
11452                 p = p ? (p + '&' + f) : f;
11453             }
11454
11455             var hs = o.headers;
11456             if(this.defaultHeaders){
11457                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11458                 if(!o.headers){
11459                     o.headers = hs;
11460                 }
11461             }
11462
11463             var cb = {
11464                 success: this.handleResponse,
11465                 failure: this.handleFailure,
11466                 scope: this,
11467                 argument: {options: o},
11468                 timeout : o.timeout || this.timeout
11469             };
11470
11471             var method = o.method||this.method||(p ? "POST" : "GET");
11472
11473             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11474                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11475             }
11476
11477             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11478                 if(o.autoAbort){
11479                     this.abort();
11480                 }
11481             }else if(this.autoAbort !== false){
11482                 this.abort();
11483             }
11484
11485             if((method == 'GET' && p) || o.xmlData){
11486                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11487                 p = '';
11488             }
11489             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11490             return this.transId;
11491         }else{
11492             Roo.callback(o.callback, o.scope, [o, null, null]);
11493             return null;
11494         }
11495     },
11496
11497     /**
11498      * Determine whether this object has a request outstanding.
11499      * @param {Number} transactionId (Optional) defaults to the last transaction
11500      * @return {Boolean} True if there is an outstanding request.
11501      */
11502     isLoading : function(transId){
11503         if(transId){
11504             return Roo.lib.Ajax.isCallInProgress(transId);
11505         }else{
11506             return this.transId ? true : false;
11507         }
11508     },
11509
11510     /**
11511      * Aborts any outstanding request.
11512      * @param {Number} transactionId (Optional) defaults to the last transaction
11513      */
11514     abort : function(transId){
11515         if(transId || this.isLoading()){
11516             Roo.lib.Ajax.abort(transId || this.transId);
11517         }
11518     },
11519
11520     // private
11521     handleResponse : function(response){
11522         this.transId = false;
11523         var options = response.argument.options;
11524         response.argument = options ? options.argument : null;
11525         this.fireEvent("requestcomplete", this, response, options);
11526         Roo.callback(options.success, options.scope, [response, options]);
11527         Roo.callback(options.callback, options.scope, [options, true, response]);
11528     },
11529
11530     // private
11531     handleFailure : function(response, e){
11532         this.transId = false;
11533         var options = response.argument.options;
11534         response.argument = options ? options.argument : null;
11535         this.fireEvent("requestexception", this, response, options, e);
11536         Roo.callback(options.failure, options.scope, [response, options]);
11537         Roo.callback(options.callback, options.scope, [options, false, response]);
11538     },
11539
11540     // private
11541     doFormUpload : function(o, ps, url){
11542         var id = Roo.id();
11543         var frame = document.createElement('iframe');
11544         frame.id = id;
11545         frame.name = id;
11546         frame.className = 'x-hidden';
11547         if(Roo.isIE){
11548             frame.src = Roo.SSL_SECURE_URL;
11549         }
11550         document.body.appendChild(frame);
11551
11552         if(Roo.isIE){
11553            document.frames[id].name = id;
11554         }
11555
11556         var form = Roo.getDom(o.form);
11557         form.target = id;
11558         form.method = 'POST';
11559         form.enctype = form.encoding = 'multipart/form-data';
11560         if(url){
11561             form.action = url;
11562         }
11563
11564         var hiddens, hd;
11565         if(ps){ // add dynamic params
11566             hiddens = [];
11567             ps = Roo.urlDecode(ps, false);
11568             for(var k in ps){
11569                 if(ps.hasOwnProperty(k)){
11570                     hd = document.createElement('input');
11571                     hd.type = 'hidden';
11572                     hd.name = k;
11573                     hd.value = ps[k];
11574                     form.appendChild(hd);
11575                     hiddens.push(hd);
11576                 }
11577             }
11578         }
11579
11580         function cb(){
11581             var r = {  // bogus response object
11582                 responseText : '',
11583                 responseXML : null
11584             };
11585
11586             r.argument = o ? o.argument : null;
11587
11588             try { //
11589                 var doc;
11590                 if(Roo.isIE){
11591                     doc = frame.contentWindow.document;
11592                 }else {
11593                     doc = (frame.contentDocument || window.frames[id].document);
11594                 }
11595                 if(doc && doc.body){
11596                     r.responseText = doc.body.innerHTML;
11597                 }
11598                 if(doc && doc.XMLDocument){
11599                     r.responseXML = doc.XMLDocument;
11600                 }else {
11601                     r.responseXML = doc;
11602                 }
11603             }
11604             catch(e) {
11605                 // ignore
11606             }
11607
11608             Roo.EventManager.removeListener(frame, 'load', cb, this);
11609
11610             this.fireEvent("requestcomplete", this, r, o);
11611             Roo.callback(o.success, o.scope, [r, o]);
11612             Roo.callback(o.callback, o.scope, [o, true, r]);
11613
11614             setTimeout(function(){document.body.removeChild(frame);}, 100);
11615         }
11616
11617         Roo.EventManager.on(frame, 'load', cb, this);
11618         form.submit();
11619
11620         if(hiddens){ // remove dynamic params
11621             for(var i = 0, len = hiddens.length; i < len; i++){
11622                 form.removeChild(hiddens[i]);
11623             }
11624         }
11625     }
11626 });
11627 /*
11628  * Based on:
11629  * Ext JS Library 1.1.1
11630  * Copyright(c) 2006-2007, Ext JS, LLC.
11631  *
11632  * Originally Released Under LGPL - original licence link has changed is not relivant.
11633  *
11634  * Fork - LGPL
11635  * <script type="text/javascript">
11636  */
11637  
11638 /**
11639  * Global Ajax request class.
11640  * 
11641  * @class Roo.Ajax
11642  * @extends Roo.data.Connection
11643  * @static
11644  * 
11645  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11646  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11647  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11648  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11649  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11650  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11651  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11652  */
11653 Roo.Ajax = new Roo.data.Connection({
11654     // fix up the docs
11655     /**
11656      * @scope Roo.Ajax
11657      * @type {Boolear} 
11658      */
11659     autoAbort : false,
11660
11661     /**
11662      * Serialize the passed form into a url encoded string
11663      * @scope Roo.Ajax
11664      * @param {String/HTMLElement} form
11665      * @return {String}
11666      */
11667     serializeForm : function(form){
11668         return Roo.lib.Ajax.serializeForm(form);
11669     }
11670 });/*
11671  * Based on:
11672  * Ext JS Library 1.1.1
11673  * Copyright(c) 2006-2007, Ext JS, LLC.
11674  *
11675  * Originally Released Under LGPL - original licence link has changed is not relivant.
11676  *
11677  * Fork - LGPL
11678  * <script type="text/javascript">
11679  */
11680
11681  
11682 /**
11683  * @class Roo.UpdateManager
11684  * @extends Roo.util.Observable
11685  * Provides AJAX-style update for Element object.<br><br>
11686  * Usage:<br>
11687  * <pre><code>
11688  * // Get it from a Roo.Element object
11689  * var el = Roo.get("foo");
11690  * var mgr = el.getUpdateManager();
11691  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11692  * ...
11693  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11694  * <br>
11695  * // or directly (returns the same UpdateManager instance)
11696  * var mgr = new Roo.UpdateManager("myElementId");
11697  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11698  * mgr.on("update", myFcnNeedsToKnow);
11699  * <br>
11700    // short handed call directly from the element object
11701    Roo.get("foo").load({
11702         url: "bar.php",
11703         scripts:true,
11704         params: "for=bar",
11705         text: "Loading Foo..."
11706    });
11707  * </code></pre>
11708  * @constructor
11709  * Create new UpdateManager directly.
11710  * @param {String/HTMLElement/Roo.Element} el The element to update
11711  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11712  */
11713 Roo.UpdateManager = function(el, forceNew){
11714     el = Roo.get(el);
11715     if(!forceNew && el.updateManager){
11716         return el.updateManager;
11717     }
11718     /**
11719      * The Element object
11720      * @type Roo.Element
11721      */
11722     this.el = el;
11723     /**
11724      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11725      * @type String
11726      */
11727     this.defaultUrl = null;
11728
11729     this.addEvents({
11730         /**
11731          * @event beforeupdate
11732          * Fired before an update is made, return false from your handler and the update is cancelled.
11733          * @param {Roo.Element} el
11734          * @param {String/Object/Function} url
11735          * @param {String/Object} params
11736          */
11737         "beforeupdate": true,
11738         /**
11739          * @event update
11740          * Fired after successful update is made.
11741          * @param {Roo.Element} el
11742          * @param {Object} oResponseObject The response Object
11743          */
11744         "update": true,
11745         /**
11746          * @event failure
11747          * Fired on update failure.
11748          * @param {Roo.Element} el
11749          * @param {Object} oResponseObject The response Object
11750          */
11751         "failure": true
11752     });
11753     var d = Roo.UpdateManager.defaults;
11754     /**
11755      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11756      * @type String
11757      */
11758     this.sslBlankUrl = d.sslBlankUrl;
11759     /**
11760      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11761      * @type Boolean
11762      */
11763     this.disableCaching = d.disableCaching;
11764     /**
11765      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11766      * @type String
11767      */
11768     this.indicatorText = d.indicatorText;
11769     /**
11770      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11771      * @type String
11772      */
11773     this.showLoadIndicator = d.showLoadIndicator;
11774     /**
11775      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11776      * @type Number
11777      */
11778     this.timeout = d.timeout;
11779
11780     /**
11781      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11782      * @type Boolean
11783      */
11784     this.loadScripts = d.loadScripts;
11785
11786     /**
11787      * Transaction object of current executing transaction
11788      */
11789     this.transaction = null;
11790
11791     /**
11792      * @private
11793      */
11794     this.autoRefreshProcId = null;
11795     /**
11796      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11797      * @type Function
11798      */
11799     this.refreshDelegate = this.refresh.createDelegate(this);
11800     /**
11801      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11802      * @type Function
11803      */
11804     this.updateDelegate = this.update.createDelegate(this);
11805     /**
11806      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11807      * @type Function
11808      */
11809     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11810     /**
11811      * @private
11812      */
11813     this.successDelegate = this.processSuccess.createDelegate(this);
11814     /**
11815      * @private
11816      */
11817     this.failureDelegate = this.processFailure.createDelegate(this);
11818
11819     if(!this.renderer){
11820      /**
11821       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11822       */
11823     this.renderer = new Roo.UpdateManager.BasicRenderer();
11824     }
11825     
11826     Roo.UpdateManager.superclass.constructor.call(this);
11827 };
11828
11829 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11830     /**
11831      * Get the Element this UpdateManager is bound to
11832      * @return {Roo.Element} The element
11833      */
11834     getEl : function(){
11835         return this.el;
11836     },
11837     /**
11838      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11839      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11840 <pre><code>
11841 um.update({<br/>
11842     url: "your-url.php",<br/>
11843     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11844     callback: yourFunction,<br/>
11845     scope: yourObject, //(optional scope)  <br/>
11846     discardUrl: false, <br/>
11847     nocache: false,<br/>
11848     text: "Loading...",<br/>
11849     timeout: 30,<br/>
11850     scripts: false<br/>
11851 });
11852 </code></pre>
11853      * The only required property is url. The optional properties nocache, text and scripts
11854      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11855      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11856      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11857      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11858      */
11859     update : function(url, params, callback, discardUrl){
11860         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11861             var method = this.method,
11862                 cfg;
11863             if(typeof url == "object"){ // must be config object
11864                 cfg = url;
11865                 url = cfg.url;
11866                 params = params || cfg.params;
11867                 callback = callback || cfg.callback;
11868                 discardUrl = discardUrl || cfg.discardUrl;
11869                 if(callback && cfg.scope){
11870                     callback = callback.createDelegate(cfg.scope);
11871                 }
11872                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11873                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11874                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11875                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11876                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11877             }
11878             this.showLoading();
11879             if(!discardUrl){
11880                 this.defaultUrl = url;
11881             }
11882             if(typeof url == "function"){
11883                 url = url.call(this);
11884             }
11885
11886             method = method || (params ? "POST" : "GET");
11887             if(method == "GET"){
11888                 url = this.prepareUrl(url);
11889             }
11890
11891             var o = Roo.apply(cfg ||{}, {
11892                 url : url,
11893                 params: params,
11894                 success: this.successDelegate,
11895                 failure: this.failureDelegate,
11896                 callback: undefined,
11897                 timeout: (this.timeout*1000),
11898                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11899             });
11900             Roo.log("updated manager called with timeout of " + o.timeout);
11901             this.transaction = Roo.Ajax.request(o);
11902         }
11903     },
11904
11905     /**
11906      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11907      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11908      * @param {String/HTMLElement} form The form Id or form element
11909      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11910      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11911      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11912      */
11913     formUpdate : function(form, url, reset, callback){
11914         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11915             if(typeof url == "function"){
11916                 url = url.call(this);
11917             }
11918             form = Roo.getDom(form);
11919             this.transaction = Roo.Ajax.request({
11920                 form: form,
11921                 url:url,
11922                 success: this.successDelegate,
11923                 failure: this.failureDelegate,
11924                 timeout: (this.timeout*1000),
11925                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11926             });
11927             this.showLoading.defer(1, this);
11928         }
11929     },
11930
11931     /**
11932      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11933      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11934      */
11935     refresh : function(callback){
11936         if(this.defaultUrl == null){
11937             return;
11938         }
11939         this.update(this.defaultUrl, null, callback, true);
11940     },
11941
11942     /**
11943      * Set this element to auto refresh.
11944      * @param {Number} interval How often to update (in seconds).
11945      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11946      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11947      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11948      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11949      */
11950     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11951         if(refreshNow){
11952             this.update(url || this.defaultUrl, params, callback, true);
11953         }
11954         if(this.autoRefreshProcId){
11955             clearInterval(this.autoRefreshProcId);
11956         }
11957         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11958     },
11959
11960     /**
11961      * Stop auto refresh on this element.
11962      */
11963      stopAutoRefresh : function(){
11964         if(this.autoRefreshProcId){
11965             clearInterval(this.autoRefreshProcId);
11966             delete this.autoRefreshProcId;
11967         }
11968     },
11969
11970     isAutoRefreshing : function(){
11971        return this.autoRefreshProcId ? true : false;
11972     },
11973     /**
11974      * Called to update the element to "Loading" state. Override to perform custom action.
11975      */
11976     showLoading : function(){
11977         if(this.showLoadIndicator){
11978             this.el.update(this.indicatorText);
11979         }
11980     },
11981
11982     /**
11983      * Adds unique parameter to query string if disableCaching = true
11984      * @private
11985      */
11986     prepareUrl : function(url){
11987         if(this.disableCaching){
11988             var append = "_dc=" + (new Date().getTime());
11989             if(url.indexOf("?") !== -1){
11990                 url += "&" + append;
11991             }else{
11992                 url += "?" + append;
11993             }
11994         }
11995         return url;
11996     },
11997
11998     /**
11999      * @private
12000      */
12001     processSuccess : function(response){
12002         this.transaction = null;
12003         if(response.argument.form && response.argument.reset){
12004             try{ // put in try/catch since some older FF releases had problems with this
12005                 response.argument.form.reset();
12006             }catch(e){}
12007         }
12008         if(this.loadScripts){
12009             this.renderer.render(this.el, response, this,
12010                 this.updateComplete.createDelegate(this, [response]));
12011         }else{
12012             this.renderer.render(this.el, response, this);
12013             this.updateComplete(response);
12014         }
12015     },
12016
12017     updateComplete : function(response){
12018         this.fireEvent("update", this.el, response);
12019         if(typeof response.argument.callback == "function"){
12020             response.argument.callback(this.el, true, response);
12021         }
12022     },
12023
12024     /**
12025      * @private
12026      */
12027     processFailure : function(response){
12028         this.transaction = null;
12029         this.fireEvent("failure", this.el, response);
12030         if(typeof response.argument.callback == "function"){
12031             response.argument.callback(this.el, false, response);
12032         }
12033     },
12034
12035     /**
12036      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12037      * @param {Object} renderer The object implementing the render() method
12038      */
12039     setRenderer : function(renderer){
12040         this.renderer = renderer;
12041     },
12042
12043     getRenderer : function(){
12044        return this.renderer;
12045     },
12046
12047     /**
12048      * Set the defaultUrl used for updates
12049      * @param {String/Function} defaultUrl The url or a function to call to get the url
12050      */
12051     setDefaultUrl : function(defaultUrl){
12052         this.defaultUrl = defaultUrl;
12053     },
12054
12055     /**
12056      * Aborts the executing transaction
12057      */
12058     abort : function(){
12059         if(this.transaction){
12060             Roo.Ajax.abort(this.transaction);
12061         }
12062     },
12063
12064     /**
12065      * Returns true if an update is in progress
12066      * @return {Boolean}
12067      */
12068     isUpdating : function(){
12069         if(this.transaction){
12070             return Roo.Ajax.isLoading(this.transaction);
12071         }
12072         return false;
12073     }
12074 });
12075
12076 /**
12077  * @class Roo.UpdateManager.defaults
12078  * @static (not really - but it helps the doc tool)
12079  * The defaults collection enables customizing the default properties of UpdateManager
12080  */
12081    Roo.UpdateManager.defaults = {
12082        /**
12083          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12084          * @type Number
12085          */
12086          timeout : 30,
12087
12088          /**
12089          * True to process scripts by default (Defaults to false).
12090          * @type Boolean
12091          */
12092         loadScripts : false,
12093
12094         /**
12095         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12096         * @type String
12097         */
12098         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12099         /**
12100          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12101          * @type Boolean
12102          */
12103         disableCaching : false,
12104         /**
12105          * Whether to show indicatorText when loading (Defaults to true).
12106          * @type Boolean
12107          */
12108         showLoadIndicator : true,
12109         /**
12110          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12111          * @type String
12112          */
12113         indicatorText : '<div class="loading-indicator">Loading...</div>'
12114    };
12115
12116 /**
12117  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12118  *Usage:
12119  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12120  * @param {String/HTMLElement/Roo.Element} el The element to update
12121  * @param {String} url The url
12122  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12123  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12124  * @static
12125  * @deprecated
12126  * @member Roo.UpdateManager
12127  */
12128 Roo.UpdateManager.updateElement = function(el, url, params, options){
12129     var um = Roo.get(el, true).getUpdateManager();
12130     Roo.apply(um, options);
12131     um.update(url, params, options ? options.callback : null);
12132 };
12133 // alias for backwards compat
12134 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12135 /**
12136  * @class Roo.UpdateManager.BasicRenderer
12137  * Default Content renderer. Updates the elements innerHTML with the responseText.
12138  */
12139 Roo.UpdateManager.BasicRenderer = function(){};
12140
12141 Roo.UpdateManager.BasicRenderer.prototype = {
12142     /**
12143      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12144      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12145      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12146      * @param {Roo.Element} el The element being rendered
12147      * @param {Object} response The YUI Connect response object
12148      * @param {UpdateManager} updateManager The calling update manager
12149      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12150      */
12151      render : function(el, response, updateManager, callback){
12152         el.update(response.responseText, updateManager.loadScripts, callback);
12153     }
12154 };
12155 /*
12156  * Based on:
12157  * Roo JS
12158  * (c)) Alan Knowles
12159  * Licence : LGPL
12160  */
12161
12162
12163 /**
12164  * @class Roo.DomTemplate
12165  * @extends Roo.Template
12166  * An effort at a dom based template engine..
12167  *
12168  * Similar to XTemplate, except it uses dom parsing to create the template..
12169  *
12170  * Supported features:
12171  *
12172  *  Tags:
12173
12174 <pre><code>
12175       {a_variable} - output encoded.
12176       {a_variable.format:("Y-m-d")} - call a method on the variable
12177       {a_variable:raw} - unencoded output
12178       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12179       {a_variable:this.method_on_template(...)} - call a method on the template object.
12180  
12181 </code></pre>
12182  *  The tpl tag:
12183 <pre><code>
12184         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12185         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12186         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12187         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12188   
12189 </code></pre>
12190  *      
12191  */
12192 Roo.DomTemplate = function()
12193 {
12194      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12195      if (this.html) {
12196         this.compile();
12197      }
12198 };
12199
12200
12201 Roo.extend(Roo.DomTemplate, Roo.Template, {
12202     /**
12203      * id counter for sub templates.
12204      */
12205     id : 0,
12206     /**
12207      * flag to indicate if dom parser is inside a pre,
12208      * it will strip whitespace if not.
12209      */
12210     inPre : false,
12211     
12212     /**
12213      * The various sub templates
12214      */
12215     tpls : false,
12216     
12217     
12218     
12219     /**
12220      *
12221      * basic tag replacing syntax
12222      * WORD:WORD()
12223      *
12224      * // you can fake an object call by doing this
12225      *  x.t:(test,tesT) 
12226      * 
12227      */
12228     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12229     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12230     
12231     iterChild : function (node, method) {
12232         
12233         var oldPre = this.inPre;
12234         if (node.tagName == 'PRE') {
12235             this.inPre = true;
12236         }
12237         for( var i = 0; i < node.childNodes.length; i++) {
12238             method.call(this, node.childNodes[i]);
12239         }
12240         this.inPre = oldPre;
12241     },
12242     
12243     
12244     
12245     /**
12246      * compile the template
12247      *
12248      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12249      *
12250      */
12251     compile: function()
12252     {
12253         var s = this.html;
12254         
12255         // covert the html into DOM...
12256         var doc = false;
12257         var div =false;
12258         try {
12259             doc = document.implementation.createHTMLDocument("");
12260             doc.documentElement.innerHTML =   this.html  ;
12261             div = doc.documentElement;
12262         } catch (e) {
12263             // old IE... - nasty -- it causes all sorts of issues.. with
12264             // images getting pulled from server..
12265             div = document.createElement('div');
12266             div.innerHTML = this.html;
12267         }
12268         //doc.documentElement.innerHTML = htmlBody
12269          
12270         
12271         
12272         this.tpls = [];
12273         var _t = this;
12274         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12275         
12276         var tpls = this.tpls;
12277         
12278         // create a top level template from the snippet..
12279         
12280         //Roo.log(div.innerHTML);
12281         
12282         var tpl = {
12283             uid : 'master',
12284             id : this.id++,
12285             attr : false,
12286             value : false,
12287             body : div.innerHTML,
12288             
12289             forCall : false,
12290             execCall : false,
12291             dom : div,
12292             isTop : true
12293             
12294         };
12295         tpls.unshift(tpl);
12296         
12297         
12298         // compile them...
12299         this.tpls = [];
12300         Roo.each(tpls, function(tp){
12301             this.compileTpl(tp);
12302             this.tpls[tp.id] = tp;
12303         }, this);
12304         
12305         this.master = tpls[0];
12306         return this;
12307         
12308         
12309     },
12310     
12311     compileNode : function(node, istop) {
12312         // test for
12313         //Roo.log(node);
12314         
12315         
12316         // skip anything not a tag..
12317         if (node.nodeType != 1) {
12318             if (node.nodeType == 3 && !this.inPre) {
12319                 // reduce white space..
12320                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12321                 
12322             }
12323             return;
12324         }
12325         
12326         var tpl = {
12327             uid : false,
12328             id : false,
12329             attr : false,
12330             value : false,
12331             body : '',
12332             
12333             forCall : false,
12334             execCall : false,
12335             dom : false,
12336             isTop : istop
12337             
12338             
12339         };
12340         
12341         
12342         switch(true) {
12343             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12344             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12345             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12346             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12347             // no default..
12348         }
12349         
12350         
12351         if (!tpl.attr) {
12352             // just itterate children..
12353             this.iterChild(node,this.compileNode);
12354             return;
12355         }
12356         tpl.uid = this.id++;
12357         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12358         node.removeAttribute('roo-'+ tpl.attr);
12359         if (tpl.attr != 'name') {
12360             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12361             node.parentNode.replaceChild(placeholder,  node);
12362         } else {
12363             
12364             var placeholder =  document.createElement('span');
12365             placeholder.className = 'roo-tpl-' + tpl.value;
12366             node.parentNode.replaceChild(placeholder,  node);
12367         }
12368         
12369         // parent now sees '{domtplXXXX}
12370         this.iterChild(node,this.compileNode);
12371         
12372         // we should now have node body...
12373         var div = document.createElement('div');
12374         div.appendChild(node);
12375         tpl.dom = node;
12376         // this has the unfortunate side effect of converting tagged attributes
12377         // eg. href="{...}" into %7C...%7D
12378         // this has been fixed by searching for those combo's although it's a bit hacky..
12379         
12380         
12381         tpl.body = div.innerHTML;
12382         
12383         
12384          
12385         tpl.id = tpl.uid;
12386         switch(tpl.attr) {
12387             case 'for' :
12388                 switch (tpl.value) {
12389                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12390                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12391                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12392                 }
12393                 break;
12394             
12395             case 'exec':
12396                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12397                 break;
12398             
12399             case 'if':     
12400                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12401                 break;
12402             
12403             case 'name':
12404                 tpl.id  = tpl.value; // replace non characters???
12405                 break;
12406             
12407         }
12408         
12409         
12410         this.tpls.push(tpl);
12411         
12412         
12413         
12414     },
12415     
12416     
12417     
12418     
12419     /**
12420      * Compile a segment of the template into a 'sub-template'
12421      *
12422      * 
12423      * 
12424      *
12425      */
12426     compileTpl : function(tpl)
12427     {
12428         var fm = Roo.util.Format;
12429         var useF = this.disableFormats !== true;
12430         
12431         var sep = Roo.isGecko ? "+\n" : ",\n";
12432         
12433         var undef = function(str) {
12434             Roo.debug && Roo.log("Property not found :"  + str);
12435             return '';
12436         };
12437           
12438         //Roo.log(tpl.body);
12439         
12440         
12441         
12442         var fn = function(m, lbrace, name, format, args)
12443         {
12444             //Roo.log("ARGS");
12445             //Roo.log(arguments);
12446             args = args ? args.replace(/\\'/g,"'") : args;
12447             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12448             if (typeof(format) == 'undefined') {
12449                 format =  'htmlEncode'; 
12450             }
12451             if (format == 'raw' ) {
12452                 format = false;
12453             }
12454             
12455             if(name.substr(0, 6) == 'domtpl'){
12456                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12457             }
12458             
12459             // build an array of options to determine if value is undefined..
12460             
12461             // basically get 'xxxx.yyyy' then do
12462             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12463             //    (function () { Roo.log("Property not found"); return ''; })() :
12464             //    ......
12465             
12466             var udef_ar = [];
12467             var lookfor = '';
12468             Roo.each(name.split('.'), function(st) {
12469                 lookfor += (lookfor.length ? '.': '') + st;
12470                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12471             });
12472             
12473             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12474             
12475             
12476             if(format && useF){
12477                 
12478                 args = args ? ',' + args : "";
12479                  
12480                 if(format.substr(0, 5) != "this."){
12481                     format = "fm." + format + '(';
12482                 }else{
12483                     format = 'this.call("'+ format.substr(5) + '", ';
12484                     args = ", values";
12485                 }
12486                 
12487                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12488             }
12489              
12490             if (args && args.length) {
12491                 // called with xxyx.yuu:(test,test)
12492                 // change to ()
12493                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12494             }
12495             // raw.. - :raw modifier..
12496             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12497             
12498         };
12499         var body;
12500         // branched to use + in gecko and [].join() in others
12501         if(Roo.isGecko){
12502             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12503                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12504                     "';};};";
12505         }else{
12506             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12507             body.push(tpl.body.replace(/(\r\n|\n)/g,
12508                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12509             body.push("'].join('');};};");
12510             body = body.join('');
12511         }
12512         
12513         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12514        
12515         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12516         eval(body);
12517         
12518         return this;
12519     },
12520      
12521     /**
12522      * same as applyTemplate, except it's done to one of the subTemplates
12523      * when using named templates, you can do:
12524      *
12525      * var str = pl.applySubTemplate('your-name', values);
12526      *
12527      * 
12528      * @param {Number} id of the template
12529      * @param {Object} values to apply to template
12530      * @param {Object} parent (normaly the instance of this object)
12531      */
12532     applySubTemplate : function(id, values, parent)
12533     {
12534         
12535         
12536         var t = this.tpls[id];
12537         
12538         
12539         try { 
12540             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12541                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12542                 return '';
12543             }
12544         } catch(e) {
12545             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12546             Roo.log(values);
12547           
12548             return '';
12549         }
12550         try { 
12551             
12552             if(t.execCall && t.execCall.call(this, values, parent)){
12553                 return '';
12554             }
12555         } catch(e) {
12556             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12557             Roo.log(values);
12558             return '';
12559         }
12560         
12561         try {
12562             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12563             parent = t.target ? values : parent;
12564             if(t.forCall && vs instanceof Array){
12565                 var buf = [];
12566                 for(var i = 0, len = vs.length; i < len; i++){
12567                     try {
12568                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12569                     } catch (e) {
12570                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12571                         Roo.log(e.body);
12572                         //Roo.log(t.compiled);
12573                         Roo.log(vs[i]);
12574                     }   
12575                 }
12576                 return buf.join('');
12577             }
12578         } catch (e) {
12579             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12580             Roo.log(values);
12581             return '';
12582         }
12583         try {
12584             return t.compiled.call(this, vs, parent);
12585         } catch (e) {
12586             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12587             Roo.log(e.body);
12588             //Roo.log(t.compiled);
12589             Roo.log(values);
12590             return '';
12591         }
12592     },
12593
12594    
12595
12596     applyTemplate : function(values){
12597         return this.master.compiled.call(this, values, {});
12598         //var s = this.subs;
12599     },
12600
12601     apply : function(){
12602         return this.applyTemplate.apply(this, arguments);
12603     }
12604
12605  });
12606
12607 Roo.DomTemplate.from = function(el){
12608     el = Roo.getDom(el);
12609     return new Roo.Domtemplate(el.value || el.innerHTML);
12610 };/*
12611  * Based on:
12612  * Ext JS Library 1.1.1
12613  * Copyright(c) 2006-2007, Ext JS, LLC.
12614  *
12615  * Originally Released Under LGPL - original licence link has changed is not relivant.
12616  *
12617  * Fork - LGPL
12618  * <script type="text/javascript">
12619  */
12620
12621 /**
12622  * @class Roo.util.DelayedTask
12623  * Provides a convenient method of performing setTimeout where a new
12624  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12625  * You can use this class to buffer
12626  * the keypress events for a certain number of milliseconds, and perform only if they stop
12627  * for that amount of time.
12628  * @constructor The parameters to this constructor serve as defaults and are not required.
12629  * @param {Function} fn (optional) The default function to timeout
12630  * @param {Object} scope (optional) The default scope of that timeout
12631  * @param {Array} args (optional) The default Array of arguments
12632  */
12633 Roo.util.DelayedTask = function(fn, scope, args){
12634     var id = null, d, t;
12635
12636     var call = function(){
12637         var now = new Date().getTime();
12638         if(now - t >= d){
12639             clearInterval(id);
12640             id = null;
12641             fn.apply(scope, args || []);
12642         }
12643     };
12644     /**
12645      * Cancels any pending timeout and queues a new one
12646      * @param {Number} delay The milliseconds to delay
12647      * @param {Function} newFn (optional) Overrides function passed to constructor
12648      * @param {Object} newScope (optional) Overrides scope passed to constructor
12649      * @param {Array} newArgs (optional) Overrides args passed to constructor
12650      */
12651     this.delay = function(delay, newFn, newScope, newArgs){
12652         if(id && delay != d){
12653             this.cancel();
12654         }
12655         d = delay;
12656         t = new Date().getTime();
12657         fn = newFn || fn;
12658         scope = newScope || scope;
12659         args = newArgs || args;
12660         if(!id){
12661             id = setInterval(call, d);
12662         }
12663     };
12664
12665     /**
12666      * Cancel the last queued timeout
12667      */
12668     this.cancel = function(){
12669         if(id){
12670             clearInterval(id);
12671             id = null;
12672         }
12673     };
12674 };/*
12675  * Based on:
12676  * Ext JS Library 1.1.1
12677  * Copyright(c) 2006-2007, Ext JS, LLC.
12678  *
12679  * Originally Released Under LGPL - original licence link has changed is not relivant.
12680  *
12681  * Fork - LGPL
12682  * <script type="text/javascript">
12683  */
12684  
12685  
12686 Roo.util.TaskRunner = function(interval){
12687     interval = interval || 10;
12688     var tasks = [], removeQueue = [];
12689     var id = 0;
12690     var running = false;
12691
12692     var stopThread = function(){
12693         running = false;
12694         clearInterval(id);
12695         id = 0;
12696     };
12697
12698     var startThread = function(){
12699         if(!running){
12700             running = true;
12701             id = setInterval(runTasks, interval);
12702         }
12703     };
12704
12705     var removeTask = function(task){
12706         removeQueue.push(task);
12707         if(task.onStop){
12708             task.onStop();
12709         }
12710     };
12711
12712     var runTasks = function(){
12713         if(removeQueue.length > 0){
12714             for(var i = 0, len = removeQueue.length; i < len; i++){
12715                 tasks.remove(removeQueue[i]);
12716             }
12717             removeQueue = [];
12718             if(tasks.length < 1){
12719                 stopThread();
12720                 return;
12721             }
12722         }
12723         var now = new Date().getTime();
12724         for(var i = 0, len = tasks.length; i < len; ++i){
12725             var t = tasks[i];
12726             var itime = now - t.taskRunTime;
12727             if(t.interval <= itime){
12728                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12729                 t.taskRunTime = now;
12730                 if(rt === false || t.taskRunCount === t.repeat){
12731                     removeTask(t);
12732                     return;
12733                 }
12734             }
12735             if(t.duration && t.duration <= (now - t.taskStartTime)){
12736                 removeTask(t);
12737             }
12738         }
12739     };
12740
12741     /**
12742      * Queues a new task.
12743      * @param {Object} task
12744      */
12745     this.start = function(task){
12746         tasks.push(task);
12747         task.taskStartTime = new Date().getTime();
12748         task.taskRunTime = 0;
12749         task.taskRunCount = 0;
12750         startThread();
12751         return task;
12752     };
12753
12754     this.stop = function(task){
12755         removeTask(task);
12756         return task;
12757     };
12758
12759     this.stopAll = function(){
12760         stopThread();
12761         for(var i = 0, len = tasks.length; i < len; i++){
12762             if(tasks[i].onStop){
12763                 tasks[i].onStop();
12764             }
12765         }
12766         tasks = [];
12767         removeQueue = [];
12768     };
12769 };
12770
12771 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12772  * Based on:
12773  * Ext JS Library 1.1.1
12774  * Copyright(c) 2006-2007, Ext JS, LLC.
12775  *
12776  * Originally Released Under LGPL - original licence link has changed is not relivant.
12777  *
12778  * Fork - LGPL
12779  * <script type="text/javascript">
12780  */
12781
12782  
12783 /**
12784  * @class Roo.util.MixedCollection
12785  * @extends Roo.util.Observable
12786  * A Collection class that maintains both numeric indexes and keys and exposes events.
12787  * @constructor
12788  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12789  * collection (defaults to false)
12790  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12791  * and return the key value for that item.  This is used when available to look up the key on items that
12792  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12793  * equivalent to providing an implementation for the {@link #getKey} method.
12794  */
12795 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12796     this.items = [];
12797     this.map = {};
12798     this.keys = [];
12799     this.length = 0;
12800     this.addEvents({
12801         /**
12802          * @event clear
12803          * Fires when the collection is cleared.
12804          */
12805         "clear" : true,
12806         /**
12807          * @event add
12808          * Fires when an item is added to the collection.
12809          * @param {Number} index The index at which the item was added.
12810          * @param {Object} o The item added.
12811          * @param {String} key The key associated with the added item.
12812          */
12813         "add" : true,
12814         /**
12815          * @event replace
12816          * Fires when an item is replaced in the collection.
12817          * @param {String} key he key associated with the new added.
12818          * @param {Object} old The item being replaced.
12819          * @param {Object} new The new item.
12820          */
12821         "replace" : true,
12822         /**
12823          * @event remove
12824          * Fires when an item is removed from the collection.
12825          * @param {Object} o The item being removed.
12826          * @param {String} key (optional) The key associated with the removed item.
12827          */
12828         "remove" : true,
12829         "sort" : true
12830     });
12831     this.allowFunctions = allowFunctions === true;
12832     if(keyFn){
12833         this.getKey = keyFn;
12834     }
12835     Roo.util.MixedCollection.superclass.constructor.call(this);
12836 };
12837
12838 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12839     allowFunctions : false,
12840     
12841 /**
12842  * Adds an item to the collection.
12843  * @param {String} key The key to associate with the item
12844  * @param {Object} o The item to add.
12845  * @return {Object} The item added.
12846  */
12847     add : function(key, o){
12848         if(arguments.length == 1){
12849             o = arguments[0];
12850             key = this.getKey(o);
12851         }
12852         if(typeof key == "undefined" || key === null){
12853             this.length++;
12854             this.items.push(o);
12855             this.keys.push(null);
12856         }else{
12857             var old = this.map[key];
12858             if(old){
12859                 return this.replace(key, o);
12860             }
12861             this.length++;
12862             this.items.push(o);
12863             this.map[key] = o;
12864             this.keys.push(key);
12865         }
12866         this.fireEvent("add", this.length-1, o, key);
12867         return o;
12868     },
12869        
12870 /**
12871   * MixedCollection has a generic way to fetch keys if you implement getKey.
12872 <pre><code>
12873 // normal way
12874 var mc = new Roo.util.MixedCollection();
12875 mc.add(someEl.dom.id, someEl);
12876 mc.add(otherEl.dom.id, otherEl);
12877 //and so on
12878
12879 // using getKey
12880 var mc = new Roo.util.MixedCollection();
12881 mc.getKey = function(el){
12882    return el.dom.id;
12883 };
12884 mc.add(someEl);
12885 mc.add(otherEl);
12886
12887 // or via the constructor
12888 var mc = new Roo.util.MixedCollection(false, function(el){
12889    return el.dom.id;
12890 });
12891 mc.add(someEl);
12892 mc.add(otherEl);
12893 </code></pre>
12894  * @param o {Object} The item for which to find the key.
12895  * @return {Object} The key for the passed item.
12896  */
12897     getKey : function(o){
12898          return o.id; 
12899     },
12900    
12901 /**
12902  * Replaces an item in the collection.
12903  * @param {String} key The key associated with the item to replace, or the item to replace.
12904  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12905  * @return {Object}  The new item.
12906  */
12907     replace : function(key, o){
12908         if(arguments.length == 1){
12909             o = arguments[0];
12910             key = this.getKey(o);
12911         }
12912         var old = this.item(key);
12913         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12914              return this.add(key, o);
12915         }
12916         var index = this.indexOfKey(key);
12917         this.items[index] = o;
12918         this.map[key] = o;
12919         this.fireEvent("replace", key, old, o);
12920         return o;
12921     },
12922    
12923 /**
12924  * Adds all elements of an Array or an Object to the collection.
12925  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12926  * an Array of values, each of which are added to the collection.
12927  */
12928     addAll : function(objs){
12929         if(arguments.length > 1 || objs instanceof Array){
12930             var args = arguments.length > 1 ? arguments : objs;
12931             for(var i = 0, len = args.length; i < len; i++){
12932                 this.add(args[i]);
12933             }
12934         }else{
12935             for(var key in objs){
12936                 if(this.allowFunctions || typeof objs[key] != "function"){
12937                     this.add(key, objs[key]);
12938                 }
12939             }
12940         }
12941     },
12942    
12943 /**
12944  * Executes the specified function once for every item in the collection, passing each
12945  * item as the first and only parameter. returning false from the function will stop the iteration.
12946  * @param {Function} fn The function to execute for each item.
12947  * @param {Object} scope (optional) The scope in which to execute the function.
12948  */
12949     each : function(fn, scope){
12950         var items = [].concat(this.items); // each safe for removal
12951         for(var i = 0, len = items.length; i < len; i++){
12952             if(fn.call(scope || items[i], items[i], i, len) === false){
12953                 break;
12954             }
12955         }
12956     },
12957    
12958 /**
12959  * Executes the specified function once for every key in the collection, passing each
12960  * key, and its associated item as the first two parameters.
12961  * @param {Function} fn The function to execute for each item.
12962  * @param {Object} scope (optional) The scope in which to execute the function.
12963  */
12964     eachKey : function(fn, scope){
12965         for(var i = 0, len = this.keys.length; i < len; i++){
12966             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12967         }
12968     },
12969    
12970 /**
12971  * Returns the first item in the collection which elicits a true return value from the
12972  * passed selection function.
12973  * @param {Function} fn The selection function to execute for each item.
12974  * @param {Object} scope (optional) The scope in which to execute the function.
12975  * @return {Object} The first item in the collection which returned true from the selection function.
12976  */
12977     find : function(fn, scope){
12978         for(var i = 0, len = this.items.length; i < len; i++){
12979             if(fn.call(scope || window, this.items[i], this.keys[i])){
12980                 return this.items[i];
12981             }
12982         }
12983         return null;
12984     },
12985    
12986 /**
12987  * Inserts an item at the specified index in the collection.
12988  * @param {Number} index The index to insert the item at.
12989  * @param {String} key The key to associate with the new item, or the item itself.
12990  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12991  * @return {Object} The item inserted.
12992  */
12993     insert : function(index, key, o){
12994         if(arguments.length == 2){
12995             o = arguments[1];
12996             key = this.getKey(o);
12997         }
12998         if(index >= this.length){
12999             return this.add(key, o);
13000         }
13001         this.length++;
13002         this.items.splice(index, 0, o);
13003         if(typeof key != "undefined" && key != null){
13004             this.map[key] = o;
13005         }
13006         this.keys.splice(index, 0, key);
13007         this.fireEvent("add", index, o, key);
13008         return o;
13009     },
13010    
13011 /**
13012  * Removed an item from the collection.
13013  * @param {Object} o The item to remove.
13014  * @return {Object} The item removed.
13015  */
13016     remove : function(o){
13017         return this.removeAt(this.indexOf(o));
13018     },
13019    
13020 /**
13021  * Remove an item from a specified index in the collection.
13022  * @param {Number} index The index within the collection of the item to remove.
13023  */
13024     removeAt : function(index){
13025         if(index < this.length && index >= 0){
13026             this.length--;
13027             var o = this.items[index];
13028             this.items.splice(index, 1);
13029             var key = this.keys[index];
13030             if(typeof key != "undefined"){
13031                 delete this.map[key];
13032             }
13033             this.keys.splice(index, 1);
13034             this.fireEvent("remove", o, key);
13035         }
13036     },
13037    
13038 /**
13039  * Removed an item associated with the passed key fom the collection.
13040  * @param {String} key The key of the item to remove.
13041  */
13042     removeKey : function(key){
13043         return this.removeAt(this.indexOfKey(key));
13044     },
13045    
13046 /**
13047  * Returns the number of items in the collection.
13048  * @return {Number} the number of items in the collection.
13049  */
13050     getCount : function(){
13051         return this.length; 
13052     },
13053    
13054 /**
13055  * Returns index within the collection of the passed Object.
13056  * @param {Object} o The item to find the index of.
13057  * @return {Number} index of the item.
13058  */
13059     indexOf : function(o){
13060         if(!this.items.indexOf){
13061             for(var i = 0, len = this.items.length; i < len; i++){
13062                 if(this.items[i] == o) return i;
13063             }
13064             return -1;
13065         }else{
13066             return this.items.indexOf(o);
13067         }
13068     },
13069    
13070 /**
13071  * Returns index within the collection of the passed key.
13072  * @param {String} key The key to find the index of.
13073  * @return {Number} index of the key.
13074  */
13075     indexOfKey : function(key){
13076         if(!this.keys.indexOf){
13077             for(var i = 0, len = this.keys.length; i < len; i++){
13078                 if(this.keys[i] == key) return i;
13079             }
13080             return -1;
13081         }else{
13082             return this.keys.indexOf(key);
13083         }
13084     },
13085    
13086 /**
13087  * Returns the item associated with the passed key OR index. Key has priority over index.
13088  * @param {String/Number} key The key or index of the item.
13089  * @return {Object} The item associated with the passed key.
13090  */
13091     item : function(key){
13092         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13093         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13094     },
13095     
13096 /**
13097  * Returns the item at the specified index.
13098  * @param {Number} index The index of the item.
13099  * @return {Object}
13100  */
13101     itemAt : function(index){
13102         return this.items[index];
13103     },
13104     
13105 /**
13106  * Returns the item associated with the passed key.
13107  * @param {String/Number} key The key of the item.
13108  * @return {Object} The item associated with the passed key.
13109  */
13110     key : function(key){
13111         return this.map[key];
13112     },
13113    
13114 /**
13115  * Returns true if the collection contains the passed Object as an item.
13116  * @param {Object} o  The Object to look for in the collection.
13117  * @return {Boolean} True if the collection contains the Object as an item.
13118  */
13119     contains : function(o){
13120         return this.indexOf(o) != -1;
13121     },
13122    
13123 /**
13124  * Returns true if the collection contains the passed Object as a key.
13125  * @param {String} key The key to look for in the collection.
13126  * @return {Boolean} True if the collection contains the Object as a key.
13127  */
13128     containsKey : function(key){
13129         return typeof this.map[key] != "undefined";
13130     },
13131    
13132 /**
13133  * Removes all items from the collection.
13134  */
13135     clear : function(){
13136         this.length = 0;
13137         this.items = [];
13138         this.keys = [];
13139         this.map = {};
13140         this.fireEvent("clear");
13141     },
13142    
13143 /**
13144  * Returns the first item in the collection.
13145  * @return {Object} the first item in the collection..
13146  */
13147     first : function(){
13148         return this.items[0]; 
13149     },
13150    
13151 /**
13152  * Returns the last item in the collection.
13153  * @return {Object} the last item in the collection..
13154  */
13155     last : function(){
13156         return this.items[this.length-1];   
13157     },
13158     
13159     _sort : function(property, dir, fn){
13160         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13161         fn = fn || function(a, b){
13162             return a-b;
13163         };
13164         var c = [], k = this.keys, items = this.items;
13165         for(var i = 0, len = items.length; i < len; i++){
13166             c[c.length] = {key: k[i], value: items[i], index: i};
13167         }
13168         c.sort(function(a, b){
13169             var v = fn(a[property], b[property]) * dsc;
13170             if(v == 0){
13171                 v = (a.index < b.index ? -1 : 1);
13172             }
13173             return v;
13174         });
13175         for(var i = 0, len = c.length; i < len; i++){
13176             items[i] = c[i].value;
13177             k[i] = c[i].key;
13178         }
13179         this.fireEvent("sort", this);
13180     },
13181     
13182     /**
13183      * Sorts this collection with the passed comparison function
13184      * @param {String} direction (optional) "ASC" or "DESC"
13185      * @param {Function} fn (optional) comparison function
13186      */
13187     sort : function(dir, fn){
13188         this._sort("value", dir, fn);
13189     },
13190     
13191     /**
13192      * Sorts this collection by keys
13193      * @param {String} direction (optional) "ASC" or "DESC"
13194      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13195      */
13196     keySort : function(dir, fn){
13197         this._sort("key", dir, fn || function(a, b){
13198             return String(a).toUpperCase()-String(b).toUpperCase();
13199         });
13200     },
13201     
13202     /**
13203      * Returns a range of items in this collection
13204      * @param {Number} startIndex (optional) defaults to 0
13205      * @param {Number} endIndex (optional) default to the last item
13206      * @return {Array} An array of items
13207      */
13208     getRange : function(start, end){
13209         var items = this.items;
13210         if(items.length < 1){
13211             return [];
13212         }
13213         start = start || 0;
13214         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13215         var r = [];
13216         if(start <= end){
13217             for(var i = start; i <= end; i++) {
13218                     r[r.length] = items[i];
13219             }
13220         }else{
13221             for(var i = start; i >= end; i--) {
13222                     r[r.length] = items[i];
13223             }
13224         }
13225         return r;
13226     },
13227         
13228     /**
13229      * Filter the <i>objects</i> in this collection by a specific property. 
13230      * Returns a new collection that has been filtered.
13231      * @param {String} property A property on your objects
13232      * @param {String/RegExp} value Either string that the property values 
13233      * should start with or a RegExp to test against the property
13234      * @return {MixedCollection} The new filtered collection
13235      */
13236     filter : function(property, value){
13237         if(!value.exec){ // not a regex
13238             value = String(value);
13239             if(value.length == 0){
13240                 return this.clone();
13241             }
13242             value = new RegExp("^" + Roo.escapeRe(value), "i");
13243         }
13244         return this.filterBy(function(o){
13245             return o && value.test(o[property]);
13246         });
13247         },
13248     
13249     /**
13250      * Filter by a function. * Returns a new collection that has been filtered.
13251      * The passed function will be called with each 
13252      * object in the collection. If the function returns true, the value is included 
13253      * otherwise it is filtered.
13254      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13255      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13256      * @return {MixedCollection} The new filtered collection
13257      */
13258     filterBy : function(fn, scope){
13259         var r = new Roo.util.MixedCollection();
13260         r.getKey = this.getKey;
13261         var k = this.keys, it = this.items;
13262         for(var i = 0, len = it.length; i < len; i++){
13263             if(fn.call(scope||this, it[i], k[i])){
13264                                 r.add(k[i], it[i]);
13265                         }
13266         }
13267         return r;
13268     },
13269     
13270     /**
13271      * Creates a duplicate of this collection
13272      * @return {MixedCollection}
13273      */
13274     clone : function(){
13275         var r = new Roo.util.MixedCollection();
13276         var k = this.keys, it = this.items;
13277         for(var i = 0, len = it.length; i < len; i++){
13278             r.add(k[i], it[i]);
13279         }
13280         r.getKey = this.getKey;
13281         return r;
13282     }
13283 });
13284 /**
13285  * Returns the item associated with the passed key or index.
13286  * @method
13287  * @param {String/Number} key The key or index of the item.
13288  * @return {Object} The item associated with the passed key.
13289  */
13290 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13291  * Based on:
13292  * Ext JS Library 1.1.1
13293  * Copyright(c) 2006-2007, Ext JS, LLC.
13294  *
13295  * Originally Released Under LGPL - original licence link has changed is not relivant.
13296  *
13297  * Fork - LGPL
13298  * <script type="text/javascript">
13299  */
13300 /**
13301  * @class Roo.util.JSON
13302  * Modified version of Douglas Crockford"s json.js that doesn"t
13303  * mess with the Object prototype 
13304  * http://www.json.org/js.html
13305  * @singleton
13306  */
13307 Roo.util.JSON = new (function(){
13308     var useHasOwn = {}.hasOwnProperty ? true : false;
13309     
13310     // crashes Safari in some instances
13311     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13312     
13313     var pad = function(n) {
13314         return n < 10 ? "0" + n : n;
13315     };
13316     
13317     var m = {
13318         "\b": '\\b',
13319         "\t": '\\t',
13320         "\n": '\\n',
13321         "\f": '\\f',
13322         "\r": '\\r',
13323         '"' : '\\"',
13324         "\\": '\\\\'
13325     };
13326
13327     var encodeString = function(s){
13328         if (/["\\\x00-\x1f]/.test(s)) {
13329             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13330                 var c = m[b];
13331                 if(c){
13332                     return c;
13333                 }
13334                 c = b.charCodeAt();
13335                 return "\\u00" +
13336                     Math.floor(c / 16).toString(16) +
13337                     (c % 16).toString(16);
13338             }) + '"';
13339         }
13340         return '"' + s + '"';
13341     };
13342     
13343     var encodeArray = function(o){
13344         var a = ["["], b, i, l = o.length, v;
13345             for (i = 0; i < l; i += 1) {
13346                 v = o[i];
13347                 switch (typeof v) {
13348                     case "undefined":
13349                     case "function":
13350                     case "unknown":
13351                         break;
13352                     default:
13353                         if (b) {
13354                             a.push(',');
13355                         }
13356                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13357                         b = true;
13358                 }
13359             }
13360             a.push("]");
13361             return a.join("");
13362     };
13363     
13364     var encodeDate = function(o){
13365         return '"' + o.getFullYear() + "-" +
13366                 pad(o.getMonth() + 1) + "-" +
13367                 pad(o.getDate()) + "T" +
13368                 pad(o.getHours()) + ":" +
13369                 pad(o.getMinutes()) + ":" +
13370                 pad(o.getSeconds()) + '"';
13371     };
13372     
13373     /**
13374      * Encodes an Object, Array or other value
13375      * @param {Mixed} o The variable to encode
13376      * @return {String} The JSON string
13377      */
13378     this.encode = function(o)
13379     {
13380         // should this be extended to fully wrap stringify..
13381         
13382         if(typeof o == "undefined" || o === null){
13383             return "null";
13384         }else if(o instanceof Array){
13385             return encodeArray(o);
13386         }else if(o instanceof Date){
13387             return encodeDate(o);
13388         }else if(typeof o == "string"){
13389             return encodeString(o);
13390         }else if(typeof o == "number"){
13391             return isFinite(o) ? String(o) : "null";
13392         }else if(typeof o == "boolean"){
13393             return String(o);
13394         }else {
13395             var a = ["{"], b, i, v;
13396             for (i in o) {
13397                 if(!useHasOwn || o.hasOwnProperty(i)) {
13398                     v = o[i];
13399                     switch (typeof v) {
13400                     case "undefined":
13401                     case "function":
13402                     case "unknown":
13403                         break;
13404                     default:
13405                         if(b){
13406                             a.push(',');
13407                         }
13408                         a.push(this.encode(i), ":",
13409                                 v === null ? "null" : this.encode(v));
13410                         b = true;
13411                     }
13412                 }
13413             }
13414             a.push("}");
13415             return a.join("");
13416         }
13417     };
13418     
13419     /**
13420      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13421      * @param {String} json The JSON string
13422      * @return {Object} The resulting object
13423      */
13424     this.decode = function(json){
13425         
13426         return  /** eval:var:json */ eval("(" + json + ')');
13427     };
13428 })();
13429 /** 
13430  * Shorthand for {@link Roo.util.JSON#encode}
13431  * @member Roo encode 
13432  * @method */
13433 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13434 /** 
13435  * Shorthand for {@link Roo.util.JSON#decode}
13436  * @member Roo decode 
13437  * @method */
13438 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13439 /*
13440  * Based on:
13441  * Ext JS Library 1.1.1
13442  * Copyright(c) 2006-2007, Ext JS, LLC.
13443  *
13444  * Originally Released Under LGPL - original licence link has changed is not relivant.
13445  *
13446  * Fork - LGPL
13447  * <script type="text/javascript">
13448  */
13449  
13450 /**
13451  * @class Roo.util.Format
13452  * Reusable data formatting functions
13453  * @singleton
13454  */
13455 Roo.util.Format = function(){
13456     var trimRe = /^\s+|\s+$/g;
13457     return {
13458         /**
13459          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13460          * @param {String} value The string to truncate
13461          * @param {Number} length The maximum length to allow before truncating
13462          * @return {String} The converted text
13463          */
13464         ellipsis : function(value, len){
13465             if(value && value.length > len){
13466                 return value.substr(0, len-3)+"...";
13467             }
13468             return value;
13469         },
13470
13471         /**
13472          * Checks a reference and converts it to empty string if it is undefined
13473          * @param {Mixed} value Reference to check
13474          * @return {Mixed} Empty string if converted, otherwise the original value
13475          */
13476         undef : function(value){
13477             return typeof value != "undefined" ? value : "";
13478         },
13479
13480         /**
13481          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13482          * @param {String} value The string to encode
13483          * @return {String} The encoded text
13484          */
13485         htmlEncode : function(value){
13486             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13487         },
13488
13489         /**
13490          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13491          * @param {String} value The string to decode
13492          * @return {String} The decoded text
13493          */
13494         htmlDecode : function(value){
13495             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13496         },
13497
13498         /**
13499          * Trims any whitespace from either side of a string
13500          * @param {String} value The text to trim
13501          * @return {String} The trimmed text
13502          */
13503         trim : function(value){
13504             return String(value).replace(trimRe, "");
13505         },
13506
13507         /**
13508          * Returns a substring from within an original string
13509          * @param {String} value The original text
13510          * @param {Number} start The start index of the substring
13511          * @param {Number} length The length of the substring
13512          * @return {String} The substring
13513          */
13514         substr : function(value, start, length){
13515             return String(value).substr(start, length);
13516         },
13517
13518         /**
13519          * Converts a string to all lower case letters
13520          * @param {String} value The text to convert
13521          * @return {String} The converted text
13522          */
13523         lowercase : function(value){
13524             return String(value).toLowerCase();
13525         },
13526
13527         /**
13528          * Converts a string to all upper case letters
13529          * @param {String} value The text to convert
13530          * @return {String} The converted text
13531          */
13532         uppercase : function(value){
13533             return String(value).toUpperCase();
13534         },
13535
13536         /**
13537          * Converts the first character only of a string to upper case
13538          * @param {String} value The text to convert
13539          * @return {String} The converted text
13540          */
13541         capitalize : function(value){
13542             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13543         },
13544
13545         // private
13546         call : function(value, fn){
13547             if(arguments.length > 2){
13548                 var args = Array.prototype.slice.call(arguments, 2);
13549                 args.unshift(value);
13550                  
13551                 return /** eval:var:value */  eval(fn).apply(window, args);
13552             }else{
13553                 /** eval:var:value */
13554                 return /** eval:var:value */ eval(fn).call(window, value);
13555             }
13556         },
13557
13558        
13559         /**
13560          * safer version of Math.toFixed..??/
13561          * @param {Number/String} value The numeric value to format
13562          * @param {Number/String} value Decimal places 
13563          * @return {String} The formatted currency string
13564          */
13565         toFixed : function(v, n)
13566         {
13567             // why not use to fixed - precision is buggered???
13568             if (!n) {
13569                 return Math.round(v-0);
13570             }
13571             var fact = Math.pow(10,n+1);
13572             v = (Math.round((v-0)*fact))/fact;
13573             var z = (''+fact).substring(2);
13574             if (v == Math.floor(v)) {
13575                 return Math.floor(v) + '.' + z;
13576             }
13577             
13578             // now just padd decimals..
13579             var ps = String(v).split('.');
13580             var fd = (ps[1] + z);
13581             var r = fd.substring(0,n); 
13582             var rm = fd.substring(n); 
13583             if (rm < 5) {
13584                 return ps[0] + '.' + r;
13585             }
13586             r*=1; // turn it into a number;
13587             r++;
13588             if (String(r).length != n) {
13589                 ps[0]*=1;
13590                 ps[0]++;
13591                 r = String(r).substring(1); // chop the end off.
13592             }
13593             
13594             return ps[0] + '.' + r;
13595              
13596         },
13597         
13598         /**
13599          * Format a number as US currency
13600          * @param {Number/String} value The numeric value to format
13601          * @return {String} The formatted currency string
13602          */
13603         usMoney : function(v){
13604             return '$' + Roo.util.Format.number(v);
13605         },
13606         
13607         /**
13608          * Format a number
13609          * eventually this should probably emulate php's number_format
13610          * @param {Number/String} value The numeric value to format
13611          * @param {Number} decimals number of decimal places
13612          * @return {String} The formatted currency string
13613          */
13614         number : function(v,decimals)
13615         {
13616             // multiply and round.
13617             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13618             var mul = Math.pow(10, decimals);
13619             var zero = String(mul).substring(1);
13620             v = (Math.round((v-0)*mul))/mul;
13621             
13622             // if it's '0' number.. then
13623             
13624             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13625             v = String(v);
13626             var ps = v.split('.');
13627             var whole = ps[0];
13628             
13629             
13630             var r = /(\d+)(\d{3})/;
13631             // add comma's
13632             while (r.test(whole)) {
13633                 whole = whole.replace(r, '$1' + ',' + '$2');
13634             }
13635             
13636             
13637             var sub = ps[1] ?
13638                     // has decimals..
13639                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13640                     // does not have decimals
13641                     (decimals ? ('.' + zero) : '');
13642             
13643             
13644             return whole + sub ;
13645         },
13646         
13647         /**
13648          * Parse a value into a formatted date using the specified format pattern.
13649          * @param {Mixed} value The value to format
13650          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13651          * @return {String} The formatted date string
13652          */
13653         date : function(v, format){
13654             if(!v){
13655                 return "";
13656             }
13657             if(!(v instanceof Date)){
13658                 v = new Date(Date.parse(v));
13659             }
13660             return v.dateFormat(format || Roo.util.Format.defaults.date);
13661         },
13662
13663         /**
13664          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13665          * @param {String} format Any valid date format string
13666          * @return {Function} The date formatting function
13667          */
13668         dateRenderer : function(format){
13669             return function(v){
13670                 return Roo.util.Format.date(v, format);  
13671             };
13672         },
13673
13674         // private
13675         stripTagsRE : /<\/?[^>]+>/gi,
13676         
13677         /**
13678          * Strips all HTML tags
13679          * @param {Mixed} value The text from which to strip tags
13680          * @return {String} The stripped text
13681          */
13682         stripTags : function(v){
13683             return !v ? v : String(v).replace(this.stripTagsRE, "");
13684         }
13685     };
13686 }();
13687 Roo.util.Format.defaults = {
13688     date : 'd/M/Y'
13689 };/*
13690  * Based on:
13691  * Ext JS Library 1.1.1
13692  * Copyright(c) 2006-2007, Ext JS, LLC.
13693  *
13694  * Originally Released Under LGPL - original licence link has changed is not relivant.
13695  *
13696  * Fork - LGPL
13697  * <script type="text/javascript">
13698  */
13699
13700
13701  
13702
13703 /**
13704  * @class Roo.MasterTemplate
13705  * @extends Roo.Template
13706  * Provides a template that can have child templates. The syntax is:
13707 <pre><code>
13708 var t = new Roo.MasterTemplate(
13709         '&lt;select name="{name}"&gt;',
13710                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13711         '&lt;/select&gt;'
13712 );
13713 t.add('options', {value: 'foo', text: 'bar'});
13714 // or you can add multiple child elements in one shot
13715 t.addAll('options', [
13716     {value: 'foo', text: 'bar'},
13717     {value: 'foo2', text: 'bar2'},
13718     {value: 'foo3', text: 'bar3'}
13719 ]);
13720 // then append, applying the master template values
13721 t.append('my-form', {name: 'my-select'});
13722 </code></pre>
13723 * A name attribute for the child template is not required if you have only one child
13724 * template or you want to refer to them by index.
13725  */
13726 Roo.MasterTemplate = function(){
13727     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13728     this.originalHtml = this.html;
13729     var st = {};
13730     var m, re = this.subTemplateRe;
13731     re.lastIndex = 0;
13732     var subIndex = 0;
13733     while(m = re.exec(this.html)){
13734         var name = m[1], content = m[2];
13735         st[subIndex] = {
13736             name: name,
13737             index: subIndex,
13738             buffer: [],
13739             tpl : new Roo.Template(content)
13740         };
13741         if(name){
13742             st[name] = st[subIndex];
13743         }
13744         st[subIndex].tpl.compile();
13745         st[subIndex].tpl.call = this.call.createDelegate(this);
13746         subIndex++;
13747     }
13748     this.subCount = subIndex;
13749     this.subs = st;
13750 };
13751 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13752     /**
13753     * The regular expression used to match sub templates
13754     * @type RegExp
13755     * @property
13756     */
13757     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13758
13759     /**
13760      * Applies the passed values to a child template.
13761      * @param {String/Number} name (optional) The name or index of the child template
13762      * @param {Array/Object} values The values to be applied to the template
13763      * @return {MasterTemplate} this
13764      */
13765      add : function(name, values){
13766         if(arguments.length == 1){
13767             values = arguments[0];
13768             name = 0;
13769         }
13770         var s = this.subs[name];
13771         s.buffer[s.buffer.length] = s.tpl.apply(values);
13772         return this;
13773     },
13774
13775     /**
13776      * Applies all the passed values to a child template.
13777      * @param {String/Number} name (optional) The name or index of the child template
13778      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13779      * @param {Boolean} reset (optional) True to reset the template first
13780      * @return {MasterTemplate} this
13781      */
13782     fill : function(name, values, reset){
13783         var a = arguments;
13784         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13785             values = a[0];
13786             name = 0;
13787             reset = a[1];
13788         }
13789         if(reset){
13790             this.reset();
13791         }
13792         for(var i = 0, len = values.length; i < len; i++){
13793             this.add(name, values[i]);
13794         }
13795         return this;
13796     },
13797
13798     /**
13799      * Resets the template for reuse
13800      * @return {MasterTemplate} this
13801      */
13802      reset : function(){
13803         var s = this.subs;
13804         for(var i = 0; i < this.subCount; i++){
13805             s[i].buffer = [];
13806         }
13807         return this;
13808     },
13809
13810     applyTemplate : function(values){
13811         var s = this.subs;
13812         var replaceIndex = -1;
13813         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13814             return s[++replaceIndex].buffer.join("");
13815         });
13816         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13817     },
13818
13819     apply : function(){
13820         return this.applyTemplate.apply(this, arguments);
13821     },
13822
13823     compile : function(){return this;}
13824 });
13825
13826 /**
13827  * Alias for fill().
13828  * @method
13829  */
13830 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13831  /**
13832  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13833  * var tpl = Roo.MasterTemplate.from('element-id');
13834  * @param {String/HTMLElement} el
13835  * @param {Object} config
13836  * @static
13837  */
13838 Roo.MasterTemplate.from = function(el, config){
13839     el = Roo.getDom(el);
13840     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13841 };/*
13842  * Based on:
13843  * Ext JS Library 1.1.1
13844  * Copyright(c) 2006-2007, Ext JS, LLC.
13845  *
13846  * Originally Released Under LGPL - original licence link has changed is not relivant.
13847  *
13848  * Fork - LGPL
13849  * <script type="text/javascript">
13850  */
13851
13852  
13853 /**
13854  * @class Roo.util.CSS
13855  * Utility class for manipulating CSS rules
13856  * @singleton
13857  */
13858 Roo.util.CSS = function(){
13859         var rules = null;
13860         var doc = document;
13861
13862     var camelRe = /(-[a-z])/gi;
13863     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13864
13865    return {
13866    /**
13867     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13868     * tag and appended to the HEAD of the document.
13869     * @param {String|Object} cssText The text containing the css rules
13870     * @param {String} id An id to add to the stylesheet for later removal
13871     * @return {StyleSheet}
13872     */
13873     createStyleSheet : function(cssText, id){
13874         var ss;
13875         var head = doc.getElementsByTagName("head")[0];
13876         var nrules = doc.createElement("style");
13877         nrules.setAttribute("type", "text/css");
13878         if(id){
13879             nrules.setAttribute("id", id);
13880         }
13881         if (typeof(cssText) != 'string') {
13882             // support object maps..
13883             // not sure if this a good idea.. 
13884             // perhaps it should be merged with the general css handling
13885             // and handle js style props.
13886             var cssTextNew = [];
13887             for(var n in cssText) {
13888                 var citems = [];
13889                 for(var k in cssText[n]) {
13890                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13891                 }
13892                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13893                 
13894             }
13895             cssText = cssTextNew.join("\n");
13896             
13897         }
13898        
13899        
13900        if(Roo.isIE){
13901            head.appendChild(nrules);
13902            ss = nrules.styleSheet;
13903            ss.cssText = cssText;
13904        }else{
13905            try{
13906                 nrules.appendChild(doc.createTextNode(cssText));
13907            }catch(e){
13908                nrules.cssText = cssText; 
13909            }
13910            head.appendChild(nrules);
13911            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13912        }
13913        this.cacheStyleSheet(ss);
13914        return ss;
13915    },
13916
13917    /**
13918     * Removes a style or link tag by id
13919     * @param {String} id The id of the tag
13920     */
13921    removeStyleSheet : function(id){
13922        var existing = doc.getElementById(id);
13923        if(existing){
13924            existing.parentNode.removeChild(existing);
13925        }
13926    },
13927
13928    /**
13929     * Dynamically swaps an existing stylesheet reference for a new one
13930     * @param {String} id The id of an existing link tag to remove
13931     * @param {String} url The href of the new stylesheet to include
13932     */
13933    swapStyleSheet : function(id, url){
13934        this.removeStyleSheet(id);
13935        var ss = doc.createElement("link");
13936        ss.setAttribute("rel", "stylesheet");
13937        ss.setAttribute("type", "text/css");
13938        ss.setAttribute("id", id);
13939        ss.setAttribute("href", url);
13940        doc.getElementsByTagName("head")[0].appendChild(ss);
13941    },
13942    
13943    /**
13944     * Refresh the rule cache if you have dynamically added stylesheets
13945     * @return {Object} An object (hash) of rules indexed by selector
13946     */
13947    refreshCache : function(){
13948        return this.getRules(true);
13949    },
13950
13951    // private
13952    cacheStyleSheet : function(stylesheet){
13953        if(!rules){
13954            rules = {};
13955        }
13956        try{// try catch for cross domain access issue
13957            var ssRules = stylesheet.cssRules || stylesheet.rules;
13958            for(var j = ssRules.length-1; j >= 0; --j){
13959                rules[ssRules[j].selectorText] = ssRules[j];
13960            }
13961        }catch(e){}
13962    },
13963    
13964    /**
13965     * Gets all css rules for the document
13966     * @param {Boolean} refreshCache true to refresh the internal cache
13967     * @return {Object} An object (hash) of rules indexed by selector
13968     */
13969    getRules : function(refreshCache){
13970                 if(rules == null || refreshCache){
13971                         rules = {};
13972                         var ds = doc.styleSheets;
13973                         for(var i =0, len = ds.length; i < len; i++){
13974                             try{
13975                         this.cacheStyleSheet(ds[i]);
13976                     }catch(e){} 
13977                 }
13978                 }
13979                 return rules;
13980         },
13981         
13982         /**
13983     * Gets an an individual CSS rule by selector(s)
13984     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13985     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13986     * @return {CSSRule} The CSS rule or null if one is not found
13987     */
13988    getRule : function(selector, refreshCache){
13989                 var rs = this.getRules(refreshCache);
13990                 if(!(selector instanceof Array)){
13991                     return rs[selector];
13992                 }
13993                 for(var i = 0; i < selector.length; i++){
13994                         if(rs[selector[i]]){
13995                                 return rs[selector[i]];
13996                         }
13997                 }
13998                 return null;
13999         },
14000         
14001         
14002         /**
14003     * Updates a rule property
14004     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14005     * @param {String} property The css property
14006     * @param {String} value The new value for the property
14007     * @return {Boolean} true If a rule was found and updated
14008     */
14009    updateRule : function(selector, property, value){
14010                 if(!(selector instanceof Array)){
14011                         var rule = this.getRule(selector);
14012                         if(rule){
14013                                 rule.style[property.replace(camelRe, camelFn)] = value;
14014                                 return true;
14015                         }
14016                 }else{
14017                         for(var i = 0; i < selector.length; i++){
14018                                 if(this.updateRule(selector[i], property, value)){
14019                                         return true;
14020                                 }
14021                         }
14022                 }
14023                 return false;
14024         }
14025    };   
14026 }();/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037  
14038
14039 /**
14040  * @class Roo.util.ClickRepeater
14041  * @extends Roo.util.Observable
14042  * 
14043  * A wrapper class which can be applied to any element. Fires a "click" event while the
14044  * mouse is pressed. The interval between firings may be specified in the config but
14045  * defaults to 10 milliseconds.
14046  * 
14047  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14048  * 
14049  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14050  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14051  * Similar to an autorepeat key delay.
14052  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14053  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14054  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14055  *           "interval" and "delay" are ignored. "immediate" is honored.
14056  * @cfg {Boolean} preventDefault True to prevent the default click event
14057  * @cfg {Boolean} stopDefault True to stop the default click event
14058  * 
14059  * @history
14060  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14061  *     2007-02-02 jvs Renamed to ClickRepeater
14062  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14063  *
14064  *  @constructor
14065  * @param {String/HTMLElement/Element} el The element to listen on
14066  * @param {Object} config
14067  **/
14068 Roo.util.ClickRepeater = function(el, config)
14069 {
14070     this.el = Roo.get(el);
14071     this.el.unselectable();
14072
14073     Roo.apply(this, config);
14074
14075     this.addEvents({
14076     /**
14077      * @event mousedown
14078      * Fires when the mouse button is depressed.
14079      * @param {Roo.util.ClickRepeater} this
14080      */
14081         "mousedown" : true,
14082     /**
14083      * @event click
14084      * Fires on a specified interval during the time the element is pressed.
14085      * @param {Roo.util.ClickRepeater} this
14086      */
14087         "click" : true,
14088     /**
14089      * @event mouseup
14090      * Fires when the mouse key is released.
14091      * @param {Roo.util.ClickRepeater} this
14092      */
14093         "mouseup" : true
14094     });
14095
14096     this.el.on("mousedown", this.handleMouseDown, this);
14097     if(this.preventDefault || this.stopDefault){
14098         this.el.on("click", function(e){
14099             if(this.preventDefault){
14100                 e.preventDefault();
14101             }
14102             if(this.stopDefault){
14103                 e.stopEvent();
14104             }
14105         }, this);
14106     }
14107
14108     // allow inline handler
14109     if(this.handler){
14110         this.on("click", this.handler,  this.scope || this);
14111     }
14112
14113     Roo.util.ClickRepeater.superclass.constructor.call(this);
14114 };
14115
14116 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14117     interval : 20,
14118     delay: 250,
14119     preventDefault : true,
14120     stopDefault : false,
14121     timer : 0,
14122
14123     // private
14124     handleMouseDown : function(){
14125         clearTimeout(this.timer);
14126         this.el.blur();
14127         if(this.pressClass){
14128             this.el.addClass(this.pressClass);
14129         }
14130         this.mousedownTime = new Date();
14131
14132         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14133         this.el.on("mouseout", this.handleMouseOut, this);
14134
14135         this.fireEvent("mousedown", this);
14136         this.fireEvent("click", this);
14137         
14138         this.timer = this.click.defer(this.delay || this.interval, this);
14139     },
14140
14141     // private
14142     click : function(){
14143         this.fireEvent("click", this);
14144         this.timer = this.click.defer(this.getInterval(), this);
14145     },
14146
14147     // private
14148     getInterval: function(){
14149         if(!this.accelerate){
14150             return this.interval;
14151         }
14152         var pressTime = this.mousedownTime.getElapsed();
14153         if(pressTime < 500){
14154             return 400;
14155         }else if(pressTime < 1700){
14156             return 320;
14157         }else if(pressTime < 2600){
14158             return 250;
14159         }else if(pressTime < 3500){
14160             return 180;
14161         }else if(pressTime < 4400){
14162             return 140;
14163         }else if(pressTime < 5300){
14164             return 80;
14165         }else if(pressTime < 6200){
14166             return 50;
14167         }else{
14168             return 10;
14169         }
14170     },
14171
14172     // private
14173     handleMouseOut : function(){
14174         clearTimeout(this.timer);
14175         if(this.pressClass){
14176             this.el.removeClass(this.pressClass);
14177         }
14178         this.el.on("mouseover", this.handleMouseReturn, this);
14179     },
14180
14181     // private
14182     handleMouseReturn : function(){
14183         this.el.un("mouseover", this.handleMouseReturn);
14184         if(this.pressClass){
14185             this.el.addClass(this.pressClass);
14186         }
14187         this.click();
14188     },
14189
14190     // private
14191     handleMouseUp : function(){
14192         clearTimeout(this.timer);
14193         this.el.un("mouseover", this.handleMouseReturn);
14194         this.el.un("mouseout", this.handleMouseOut);
14195         Roo.get(document).un("mouseup", this.handleMouseUp);
14196         this.el.removeClass(this.pressClass);
14197         this.fireEvent("mouseup", this);
14198     }
14199 });/*
14200  * Based on:
14201  * Ext JS Library 1.1.1
14202  * Copyright(c) 2006-2007, Ext JS, LLC.
14203  *
14204  * Originally Released Under LGPL - original licence link has changed is not relivant.
14205  *
14206  * Fork - LGPL
14207  * <script type="text/javascript">
14208  */
14209
14210  
14211 /**
14212  * @class Roo.KeyNav
14213  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14214  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14215  * way to implement custom navigation schemes for any UI component.</p>
14216  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14217  * pageUp, pageDown, del, home, end.  Usage:</p>
14218  <pre><code>
14219 var nav = new Roo.KeyNav("my-element", {
14220     "left" : function(e){
14221         this.moveLeft(e.ctrlKey);
14222     },
14223     "right" : function(e){
14224         this.moveRight(e.ctrlKey);
14225     },
14226     "enter" : function(e){
14227         this.save();
14228     },
14229     scope : this
14230 });
14231 </code></pre>
14232  * @constructor
14233  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14234  * @param {Object} config The config
14235  */
14236 Roo.KeyNav = function(el, config){
14237     this.el = Roo.get(el);
14238     Roo.apply(this, config);
14239     if(!this.disabled){
14240         this.disabled = true;
14241         this.enable();
14242     }
14243 };
14244
14245 Roo.KeyNav.prototype = {
14246     /**
14247      * @cfg {Boolean} disabled
14248      * True to disable this KeyNav instance (defaults to false)
14249      */
14250     disabled : false,
14251     /**
14252      * @cfg {String} defaultEventAction
14253      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14254      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14255      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14256      */
14257     defaultEventAction: "stopEvent",
14258     /**
14259      * @cfg {Boolean} forceKeyDown
14260      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14261      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14262      * handle keydown instead of keypress.
14263      */
14264     forceKeyDown : false,
14265
14266     // private
14267     prepareEvent : function(e){
14268         var k = e.getKey();
14269         var h = this.keyToHandler[k];
14270         //if(h && this[h]){
14271         //    e.stopPropagation();
14272         //}
14273         if(Roo.isSafari && h && k >= 37 && k <= 40){
14274             e.stopEvent();
14275         }
14276     },
14277
14278     // private
14279     relay : function(e){
14280         var k = e.getKey();
14281         var h = this.keyToHandler[k];
14282         if(h && this[h]){
14283             if(this.doRelay(e, this[h], h) !== true){
14284                 e[this.defaultEventAction]();
14285             }
14286         }
14287     },
14288
14289     // private
14290     doRelay : function(e, h, hname){
14291         return h.call(this.scope || this, e);
14292     },
14293
14294     // possible handlers
14295     enter : false,
14296     left : false,
14297     right : false,
14298     up : false,
14299     down : false,
14300     tab : false,
14301     esc : false,
14302     pageUp : false,
14303     pageDown : false,
14304     del : false,
14305     home : false,
14306     end : false,
14307
14308     // quick lookup hash
14309     keyToHandler : {
14310         37 : "left",
14311         39 : "right",
14312         38 : "up",
14313         40 : "down",
14314         33 : "pageUp",
14315         34 : "pageDown",
14316         46 : "del",
14317         36 : "home",
14318         35 : "end",
14319         13 : "enter",
14320         27 : "esc",
14321         9  : "tab"
14322     },
14323
14324         /**
14325          * Enable this KeyNav
14326          */
14327         enable: function(){
14328                 if(this.disabled){
14329             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14330             // the EventObject will normalize Safari automatically
14331             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14332                 this.el.on("keydown", this.relay,  this);
14333             }else{
14334                 this.el.on("keydown", this.prepareEvent,  this);
14335                 this.el.on("keypress", this.relay,  this);
14336             }
14337                     this.disabled = false;
14338                 }
14339         },
14340
14341         /**
14342          * Disable this KeyNav
14343          */
14344         disable: function(){
14345                 if(!this.disabled){
14346                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14347                 this.el.un("keydown", this.relay);
14348             }else{
14349                 this.el.un("keydown", this.prepareEvent);
14350                 this.el.un("keypress", this.relay);
14351             }
14352                     this.disabled = true;
14353                 }
14354         }
14355 };/*
14356  * Based on:
14357  * Ext JS Library 1.1.1
14358  * Copyright(c) 2006-2007, Ext JS, LLC.
14359  *
14360  * Originally Released Under LGPL - original licence link has changed is not relivant.
14361  *
14362  * Fork - LGPL
14363  * <script type="text/javascript">
14364  */
14365
14366  
14367 /**
14368  * @class Roo.KeyMap
14369  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14370  * The constructor accepts the same config object as defined by {@link #addBinding}.
14371  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14372  * combination it will call the function with this signature (if the match is a multi-key
14373  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14374  * A KeyMap can also handle a string representation of keys.<br />
14375  * Usage:
14376  <pre><code>
14377 // map one key by key code
14378 var map = new Roo.KeyMap("my-element", {
14379     key: 13, // or Roo.EventObject.ENTER
14380     fn: myHandler,
14381     scope: myObject
14382 });
14383
14384 // map multiple keys to one action by string
14385 var map = new Roo.KeyMap("my-element", {
14386     key: "a\r\n\t",
14387     fn: myHandler,
14388     scope: myObject
14389 });
14390
14391 // map multiple keys to multiple actions by strings and array of codes
14392 var map = new Roo.KeyMap("my-element", [
14393     {
14394         key: [10,13],
14395         fn: function(){ alert("Return was pressed"); }
14396     }, {
14397         key: "abc",
14398         fn: function(){ alert('a, b or c was pressed'); }
14399     }, {
14400         key: "\t",
14401         ctrl:true,
14402         shift:true,
14403         fn: function(){ alert('Control + shift + tab was pressed.'); }
14404     }
14405 ]);
14406 </code></pre>
14407  * <b>Note: A KeyMap starts enabled</b>
14408  * @constructor
14409  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14410  * @param {Object} config The config (see {@link #addBinding})
14411  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14412  */
14413 Roo.KeyMap = function(el, config, eventName){
14414     this.el  = Roo.get(el);
14415     this.eventName = eventName || "keydown";
14416     this.bindings = [];
14417     if(config){
14418         this.addBinding(config);
14419     }
14420     this.enable();
14421 };
14422
14423 Roo.KeyMap.prototype = {
14424     /**
14425      * True to stop the event from bubbling and prevent the default browser action if the
14426      * key was handled by the KeyMap (defaults to false)
14427      * @type Boolean
14428      */
14429     stopEvent : false,
14430
14431     /**
14432      * Add a new binding to this KeyMap. The following config object properties are supported:
14433      * <pre>
14434 Property    Type             Description
14435 ----------  ---------------  ----------------------------------------------------------------------
14436 key         String/Array     A single keycode or an array of keycodes to handle
14437 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14438 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14439 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14440 fn          Function         The function to call when KeyMap finds the expected key combination
14441 scope       Object           The scope of the callback function
14442 </pre>
14443      *
14444      * Usage:
14445      * <pre><code>
14446 // Create a KeyMap
14447 var map = new Roo.KeyMap(document, {
14448     key: Roo.EventObject.ENTER,
14449     fn: handleKey,
14450     scope: this
14451 });
14452
14453 //Add a new binding to the existing KeyMap later
14454 map.addBinding({
14455     key: 'abc',
14456     shift: true,
14457     fn: handleKey,
14458     scope: this
14459 });
14460 </code></pre>
14461      * @param {Object/Array} config A single KeyMap config or an array of configs
14462      */
14463         addBinding : function(config){
14464         if(config instanceof Array){
14465             for(var i = 0, len = config.length; i < len; i++){
14466                 this.addBinding(config[i]);
14467             }
14468             return;
14469         }
14470         var keyCode = config.key,
14471             shift = config.shift, 
14472             ctrl = config.ctrl, 
14473             alt = config.alt,
14474             fn = config.fn,
14475             scope = config.scope;
14476         if(typeof keyCode == "string"){
14477             var ks = [];
14478             var keyString = keyCode.toUpperCase();
14479             for(var j = 0, len = keyString.length; j < len; j++){
14480                 ks.push(keyString.charCodeAt(j));
14481             }
14482             keyCode = ks;
14483         }
14484         var keyArray = keyCode instanceof Array;
14485         var handler = function(e){
14486             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14487                 var k = e.getKey();
14488                 if(keyArray){
14489                     for(var i = 0, len = keyCode.length; i < len; i++){
14490                         if(keyCode[i] == k){
14491                           if(this.stopEvent){
14492                               e.stopEvent();
14493                           }
14494                           fn.call(scope || window, k, e);
14495                           return;
14496                         }
14497                     }
14498                 }else{
14499                     if(k == keyCode){
14500                         if(this.stopEvent){
14501                            e.stopEvent();
14502                         }
14503                         fn.call(scope || window, k, e);
14504                     }
14505                 }
14506             }
14507         };
14508         this.bindings.push(handler);  
14509         },
14510
14511     /**
14512      * Shorthand for adding a single key listener
14513      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14514      * following options:
14515      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14516      * @param {Function} fn The function to call
14517      * @param {Object} scope (optional) The scope of the function
14518      */
14519     on : function(key, fn, scope){
14520         var keyCode, shift, ctrl, alt;
14521         if(typeof key == "object" && !(key instanceof Array)){
14522             keyCode = key.key;
14523             shift = key.shift;
14524             ctrl = key.ctrl;
14525             alt = key.alt;
14526         }else{
14527             keyCode = key;
14528         }
14529         this.addBinding({
14530             key: keyCode,
14531             shift: shift,
14532             ctrl: ctrl,
14533             alt: alt,
14534             fn: fn,
14535             scope: scope
14536         })
14537     },
14538
14539     // private
14540     handleKeyDown : function(e){
14541             if(this.enabled){ //just in case
14542             var b = this.bindings;
14543             for(var i = 0, len = b.length; i < len; i++){
14544                 b[i].call(this, e);
14545             }
14546             }
14547         },
14548         
14549         /**
14550          * Returns true if this KeyMap is enabled
14551          * @return {Boolean} 
14552          */
14553         isEnabled : function(){
14554             return this.enabled;  
14555         },
14556         
14557         /**
14558          * Enables this KeyMap
14559          */
14560         enable: function(){
14561                 if(!this.enabled){
14562                     this.el.on(this.eventName, this.handleKeyDown, this);
14563                     this.enabled = true;
14564                 }
14565         },
14566
14567         /**
14568          * Disable this KeyMap
14569          */
14570         disable: function(){
14571                 if(this.enabled){
14572                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14573                     this.enabled = false;
14574                 }
14575         }
14576 };/*
14577  * Based on:
14578  * Ext JS Library 1.1.1
14579  * Copyright(c) 2006-2007, Ext JS, LLC.
14580  *
14581  * Originally Released Under LGPL - original licence link has changed is not relivant.
14582  *
14583  * Fork - LGPL
14584  * <script type="text/javascript">
14585  */
14586
14587  
14588 /**
14589  * @class Roo.util.TextMetrics
14590  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14591  * wide, in pixels, a given block of text will be.
14592  * @singleton
14593  */
14594 Roo.util.TextMetrics = function(){
14595     var shared;
14596     return {
14597         /**
14598          * Measures the size of the specified text
14599          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14600          * that can affect the size of the rendered text
14601          * @param {String} text The text to measure
14602          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14603          * in order to accurately measure the text height
14604          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14605          */
14606         measure : function(el, text, fixedWidth){
14607             if(!shared){
14608                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14609             }
14610             shared.bind(el);
14611             shared.setFixedWidth(fixedWidth || 'auto');
14612             return shared.getSize(text);
14613         },
14614
14615         /**
14616          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14617          * the overhead of multiple calls to initialize the style properties on each measurement.
14618          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14619          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14620          * in order to accurately measure the text height
14621          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14622          */
14623         createInstance : function(el, fixedWidth){
14624             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14625         }
14626     };
14627 }();
14628
14629  
14630
14631 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14632     var ml = new Roo.Element(document.createElement('div'));
14633     document.body.appendChild(ml.dom);
14634     ml.position('absolute');
14635     ml.setLeftTop(-1000, -1000);
14636     ml.hide();
14637
14638     if(fixedWidth){
14639         ml.setWidth(fixedWidth);
14640     }
14641      
14642     var instance = {
14643         /**
14644          * Returns the size of the specified text based on the internal element's style and width properties
14645          * @memberOf Roo.util.TextMetrics.Instance#
14646          * @param {String} text The text to measure
14647          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14648          */
14649         getSize : function(text){
14650             ml.update(text);
14651             var s = ml.getSize();
14652             ml.update('');
14653             return s;
14654         },
14655
14656         /**
14657          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14658          * that can affect the size of the rendered text
14659          * @memberOf Roo.util.TextMetrics.Instance#
14660          * @param {String/HTMLElement} el The element, dom node or id
14661          */
14662         bind : function(el){
14663             ml.setStyle(
14664                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14665             );
14666         },
14667
14668         /**
14669          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14670          * to set a fixed width in order to accurately measure the text height.
14671          * @memberOf Roo.util.TextMetrics.Instance#
14672          * @param {Number} width The width to set on the element
14673          */
14674         setFixedWidth : function(width){
14675             ml.setWidth(width);
14676         },
14677
14678         /**
14679          * Returns the measured width of the specified text
14680          * @memberOf Roo.util.TextMetrics.Instance#
14681          * @param {String} text The text to measure
14682          * @return {Number} width The width in pixels
14683          */
14684         getWidth : function(text){
14685             ml.dom.style.width = 'auto';
14686             return this.getSize(text).width;
14687         },
14688
14689         /**
14690          * Returns the measured height of the specified text.  For multiline text, be sure to call
14691          * {@link #setFixedWidth} if necessary.
14692          * @memberOf Roo.util.TextMetrics.Instance#
14693          * @param {String} text The text to measure
14694          * @return {Number} height The height in pixels
14695          */
14696         getHeight : function(text){
14697             return this.getSize(text).height;
14698         }
14699     };
14700
14701     instance.bind(bindTo);
14702
14703     return instance;
14704 };
14705
14706 // backwards compat
14707 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14708  * Based on:
14709  * Ext JS Library 1.1.1
14710  * Copyright(c) 2006-2007, Ext JS, LLC.
14711  *
14712  * Originally Released Under LGPL - original licence link has changed is not relivant.
14713  *
14714  * Fork - LGPL
14715  * <script type="text/javascript">
14716  */
14717
14718 /**
14719  * @class Roo.state.Provider
14720  * Abstract base class for state provider implementations. This class provides methods
14721  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14722  * Provider interface.
14723  */
14724 Roo.state.Provider = function(){
14725     /**
14726      * @event statechange
14727      * Fires when a state change occurs.
14728      * @param {Provider} this This state provider
14729      * @param {String} key The state key which was changed
14730      * @param {String} value The encoded value for the state
14731      */
14732     this.addEvents({
14733         "statechange": true
14734     });
14735     this.state = {};
14736     Roo.state.Provider.superclass.constructor.call(this);
14737 };
14738 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14739     /**
14740      * Returns the current value for a key
14741      * @param {String} name The key name
14742      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14743      * @return {Mixed} The state data
14744      */
14745     get : function(name, defaultValue){
14746         return typeof this.state[name] == "undefined" ?
14747             defaultValue : this.state[name];
14748     },
14749     
14750     /**
14751      * Clears a value from the state
14752      * @param {String} name The key name
14753      */
14754     clear : function(name){
14755         delete this.state[name];
14756         this.fireEvent("statechange", this, name, null);
14757     },
14758     
14759     /**
14760      * Sets the value for a key
14761      * @param {String} name The key name
14762      * @param {Mixed} value The value to set
14763      */
14764     set : function(name, value){
14765         this.state[name] = value;
14766         this.fireEvent("statechange", this, name, value);
14767     },
14768     
14769     /**
14770      * Decodes a string previously encoded with {@link #encodeValue}.
14771      * @param {String} value The value to decode
14772      * @return {Mixed} The decoded value
14773      */
14774     decodeValue : function(cookie){
14775         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14776         var matches = re.exec(unescape(cookie));
14777         if(!matches || !matches[1]) return; // non state cookie
14778         var type = matches[1];
14779         var v = matches[2];
14780         switch(type){
14781             case "n":
14782                 return parseFloat(v);
14783             case "d":
14784                 return new Date(Date.parse(v));
14785             case "b":
14786                 return (v == "1");
14787             case "a":
14788                 var all = [];
14789                 var values = v.split("^");
14790                 for(var i = 0, len = values.length; i < len; i++){
14791                     all.push(this.decodeValue(values[i]));
14792                 }
14793                 return all;
14794            case "o":
14795                 var all = {};
14796                 var values = v.split("^");
14797                 for(var i = 0, len = values.length; i < len; i++){
14798                     var kv = values[i].split("=");
14799                     all[kv[0]] = this.decodeValue(kv[1]);
14800                 }
14801                 return all;
14802            default:
14803                 return v;
14804         }
14805     },
14806     
14807     /**
14808      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14809      * @param {Mixed} value The value to encode
14810      * @return {String} The encoded value
14811      */
14812     encodeValue : function(v){
14813         var enc;
14814         if(typeof v == "number"){
14815             enc = "n:" + v;
14816         }else if(typeof v == "boolean"){
14817             enc = "b:" + (v ? "1" : "0");
14818         }else if(v instanceof Date){
14819             enc = "d:" + v.toGMTString();
14820         }else if(v instanceof Array){
14821             var flat = "";
14822             for(var i = 0, len = v.length; i < len; i++){
14823                 flat += this.encodeValue(v[i]);
14824                 if(i != len-1) flat += "^";
14825             }
14826             enc = "a:" + flat;
14827         }else if(typeof v == "object"){
14828             var flat = "";
14829             for(var key in v){
14830                 if(typeof v[key] != "function"){
14831                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14832                 }
14833             }
14834             enc = "o:" + flat.substring(0, flat.length-1);
14835         }else{
14836             enc = "s:" + v;
14837         }
14838         return escape(enc);        
14839     }
14840 });
14841
14842 /*
14843  * Based on:
14844  * Ext JS Library 1.1.1
14845  * Copyright(c) 2006-2007, Ext JS, LLC.
14846  *
14847  * Originally Released Under LGPL - original licence link has changed is not relivant.
14848  *
14849  * Fork - LGPL
14850  * <script type="text/javascript">
14851  */
14852 /**
14853  * @class Roo.state.Manager
14854  * This is the global state manager. By default all components that are "state aware" check this class
14855  * for state information if you don't pass them a custom state provider. In order for this class
14856  * to be useful, it must be initialized with a provider when your application initializes.
14857  <pre><code>
14858 // in your initialization function
14859 init : function(){
14860    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14861    ...
14862    // supposed you have a {@link Roo.BorderLayout}
14863    var layout = new Roo.BorderLayout(...);
14864    layout.restoreState();
14865    // or a {Roo.BasicDialog}
14866    var dialog = new Roo.BasicDialog(...);
14867    dialog.restoreState();
14868  </code></pre>
14869  * @singleton
14870  */
14871 Roo.state.Manager = function(){
14872     var provider = new Roo.state.Provider();
14873     
14874     return {
14875         /**
14876          * Configures the default state provider for your application
14877          * @param {Provider} stateProvider The state provider to set
14878          */
14879         setProvider : function(stateProvider){
14880             provider = stateProvider;
14881         },
14882         
14883         /**
14884          * Returns the current value for a key
14885          * @param {String} name The key name
14886          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14887          * @return {Mixed} The state data
14888          */
14889         get : function(key, defaultValue){
14890             return provider.get(key, defaultValue);
14891         },
14892         
14893         /**
14894          * Sets the value for a key
14895          * @param {String} name The key name
14896          * @param {Mixed} value The state data
14897          */
14898          set : function(key, value){
14899             provider.set(key, value);
14900         },
14901         
14902         /**
14903          * Clears a value from the state
14904          * @param {String} name The key name
14905          */
14906         clear : function(key){
14907             provider.clear(key);
14908         },
14909         
14910         /**
14911          * Gets the currently configured state provider
14912          * @return {Provider} The state provider
14913          */
14914         getProvider : function(){
14915             return provider;
14916         }
14917     };
14918 }();
14919 /*
14920  * Based on:
14921  * Ext JS Library 1.1.1
14922  * Copyright(c) 2006-2007, Ext JS, LLC.
14923  *
14924  * Originally Released Under LGPL - original licence link has changed is not relivant.
14925  *
14926  * Fork - LGPL
14927  * <script type="text/javascript">
14928  */
14929 /**
14930  * @class Roo.state.CookieProvider
14931  * @extends Roo.state.Provider
14932  * The default Provider implementation which saves state via cookies.
14933  * <br />Usage:
14934  <pre><code>
14935    var cp = new Roo.state.CookieProvider({
14936        path: "/cgi-bin/",
14937        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14938        domain: "roojs.com"
14939    })
14940    Roo.state.Manager.setProvider(cp);
14941  </code></pre>
14942  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14943  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14944  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14945  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14946  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14947  * domain the page is running on including the 'www' like 'www.roojs.com')
14948  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14949  * @constructor
14950  * Create a new CookieProvider
14951  * @param {Object} config The configuration object
14952  */
14953 Roo.state.CookieProvider = function(config){
14954     Roo.state.CookieProvider.superclass.constructor.call(this);
14955     this.path = "/";
14956     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14957     this.domain = null;
14958     this.secure = false;
14959     Roo.apply(this, config);
14960     this.state = this.readCookies();
14961 };
14962
14963 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14964     // private
14965     set : function(name, value){
14966         if(typeof value == "undefined" || value === null){
14967             this.clear(name);
14968             return;
14969         }
14970         this.setCookie(name, value);
14971         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14972     },
14973
14974     // private
14975     clear : function(name){
14976         this.clearCookie(name);
14977         Roo.state.CookieProvider.superclass.clear.call(this, name);
14978     },
14979
14980     // private
14981     readCookies : function(){
14982         var cookies = {};
14983         var c = document.cookie + ";";
14984         var re = /\s?(.*?)=(.*?);/g;
14985         var matches;
14986         while((matches = re.exec(c)) != null){
14987             var name = matches[1];
14988             var value = matches[2];
14989             if(name && name.substring(0,3) == "ys-"){
14990                 cookies[name.substr(3)] = this.decodeValue(value);
14991             }
14992         }
14993         return cookies;
14994     },
14995
14996     // private
14997     setCookie : function(name, value){
14998         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14999            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15000            ((this.path == null) ? "" : ("; path=" + this.path)) +
15001            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15002            ((this.secure == true) ? "; secure" : "");
15003     },
15004
15005     // private
15006     clearCookie : function(name){
15007         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15008            ((this.path == null) ? "" : ("; path=" + this.path)) +
15009            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15010            ((this.secure == true) ? "; secure" : "");
15011     }
15012 });/*
15013  * Based on:
15014  * Ext JS Library 1.1.1
15015  * Copyright(c) 2006-2007, Ext JS, LLC.
15016  *
15017  * Originally Released Under LGPL - original licence link has changed is not relivant.
15018  *
15019  * Fork - LGPL
15020  * <script type="text/javascript">
15021  */
15022  
15023
15024 /**
15025  * @class Roo.ComponentMgr
15026  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15027  * @singleton
15028  */
15029 Roo.ComponentMgr = function(){
15030     var all = new Roo.util.MixedCollection();
15031
15032     return {
15033         /**
15034          * Registers a component.
15035          * @param {Roo.Component} c The component
15036          */
15037         register : function(c){
15038             all.add(c);
15039         },
15040
15041         /**
15042          * Unregisters a component.
15043          * @param {Roo.Component} c The component
15044          */
15045         unregister : function(c){
15046             all.remove(c);
15047         },
15048
15049         /**
15050          * Returns a component by id
15051          * @param {String} id The component id
15052          */
15053         get : function(id){
15054             return all.get(id);
15055         },
15056
15057         /**
15058          * Registers a function that will be called when a specified component is added to ComponentMgr
15059          * @param {String} id The component id
15060          * @param {Funtction} fn The callback function
15061          * @param {Object} scope The scope of the callback
15062          */
15063         onAvailable : function(id, fn, scope){
15064             all.on("add", function(index, o){
15065                 if(o.id == id){
15066                     fn.call(scope || o, o);
15067                     all.un("add", fn, scope);
15068                 }
15069             });
15070         }
15071     };
15072 }();/*
15073  * Based on:
15074  * Ext JS Library 1.1.1
15075  * Copyright(c) 2006-2007, Ext JS, LLC.
15076  *
15077  * Originally Released Under LGPL - original licence link has changed is not relivant.
15078  *
15079  * Fork - LGPL
15080  * <script type="text/javascript">
15081  */
15082  
15083 /**
15084  * @class Roo.Component
15085  * @extends Roo.util.Observable
15086  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15087  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15088  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15089  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15090  * All visual components (widgets) that require rendering into a layout should subclass Component.
15091  * @constructor
15092  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15093  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15094  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15095  */
15096 Roo.Component = function(config){
15097     config = config || {};
15098     if(config.tagName || config.dom || typeof config == "string"){ // element object
15099         config = {el: config, id: config.id || config};
15100     }
15101     this.initialConfig = config;
15102
15103     Roo.apply(this, config);
15104     this.addEvents({
15105         /**
15106          * @event disable
15107          * Fires after the component is disabled.
15108              * @param {Roo.Component} this
15109              */
15110         disable : true,
15111         /**
15112          * @event enable
15113          * Fires after the component is enabled.
15114              * @param {Roo.Component} this
15115              */
15116         enable : true,
15117         /**
15118          * @event beforeshow
15119          * Fires before the component is shown.  Return false to stop the show.
15120              * @param {Roo.Component} this
15121              */
15122         beforeshow : true,
15123         /**
15124          * @event show
15125          * Fires after the component is shown.
15126              * @param {Roo.Component} this
15127              */
15128         show : true,
15129         /**
15130          * @event beforehide
15131          * Fires before the component is hidden. Return false to stop the hide.
15132              * @param {Roo.Component} this
15133              */
15134         beforehide : true,
15135         /**
15136          * @event hide
15137          * Fires after the component is hidden.
15138              * @param {Roo.Component} this
15139              */
15140         hide : true,
15141         /**
15142          * @event beforerender
15143          * Fires before the component is rendered. Return false to stop the render.
15144              * @param {Roo.Component} this
15145              */
15146         beforerender : true,
15147         /**
15148          * @event render
15149          * Fires after the component is rendered.
15150              * @param {Roo.Component} this
15151              */
15152         render : true,
15153         /**
15154          * @event beforedestroy
15155          * Fires before the component is destroyed. Return false to stop the destroy.
15156              * @param {Roo.Component} this
15157              */
15158         beforedestroy : true,
15159         /**
15160          * @event destroy
15161          * Fires after the component is destroyed.
15162              * @param {Roo.Component} this
15163              */
15164         destroy : true
15165     });
15166     if(!this.id){
15167         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15168     }
15169     Roo.ComponentMgr.register(this);
15170     Roo.Component.superclass.constructor.call(this);
15171     this.initComponent();
15172     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15173         this.render(this.renderTo);
15174         delete this.renderTo;
15175     }
15176 };
15177
15178 /** @private */
15179 Roo.Component.AUTO_ID = 1000;
15180
15181 Roo.extend(Roo.Component, Roo.util.Observable, {
15182     /**
15183      * @scope Roo.Component.prototype
15184      * @type {Boolean}
15185      * true if this component is hidden. Read-only.
15186      */
15187     hidden : false,
15188     /**
15189      * @type {Boolean}
15190      * true if this component is disabled. Read-only.
15191      */
15192     disabled : false,
15193     /**
15194      * @type {Boolean}
15195      * true if this component has been rendered. Read-only.
15196      */
15197     rendered : false,
15198     
15199     /** @cfg {String} disableClass
15200      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15201      */
15202     disabledClass : "x-item-disabled",
15203         /** @cfg {Boolean} allowDomMove
15204          * Whether the component can move the Dom node when rendering (defaults to true).
15205          */
15206     allowDomMove : true,
15207     /** @cfg {String} hideMode
15208      * How this component should hidden. Supported values are
15209      * "visibility" (css visibility), "offsets" (negative offset position) and
15210      * "display" (css display) - defaults to "display".
15211      */
15212     hideMode: 'display',
15213
15214     /** @private */
15215     ctype : "Roo.Component",
15216
15217     /**
15218      * @cfg {String} actionMode 
15219      * which property holds the element that used for  hide() / show() / disable() / enable()
15220      * default is 'el' 
15221      */
15222     actionMode : "el",
15223
15224     /** @private */
15225     getActionEl : function(){
15226         return this[this.actionMode];
15227     },
15228
15229     initComponent : Roo.emptyFn,
15230     /**
15231      * If this is a lazy rendering component, render it to its container element.
15232      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15233      */
15234     render : function(container, position){
15235         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15236             if(!container && this.el){
15237                 this.el = Roo.get(this.el);
15238                 container = this.el.dom.parentNode;
15239                 this.allowDomMove = false;
15240             }
15241             this.container = Roo.get(container);
15242             this.rendered = true;
15243             if(position !== undefined){
15244                 if(typeof position == 'number'){
15245                     position = this.container.dom.childNodes[position];
15246                 }else{
15247                     position = Roo.getDom(position);
15248                 }
15249             }
15250             this.onRender(this.container, position || null);
15251             if(this.cls){
15252                 this.el.addClass(this.cls);
15253                 delete this.cls;
15254             }
15255             if(this.style){
15256                 this.el.applyStyles(this.style);
15257                 delete this.style;
15258             }
15259             this.fireEvent("render", this);
15260             this.afterRender(this.container);
15261             if(this.hidden){
15262                 this.hide();
15263             }
15264             if(this.disabled){
15265                 this.disable();
15266             }
15267         }
15268         return this;
15269     },
15270
15271     /** @private */
15272     // default function is not really useful
15273     onRender : function(ct, position){
15274         if(this.el){
15275             this.el = Roo.get(this.el);
15276             if(this.allowDomMove !== false){
15277                 ct.dom.insertBefore(this.el.dom, position);
15278             }
15279         }
15280     },
15281
15282     /** @private */
15283     getAutoCreate : function(){
15284         var cfg = typeof this.autoCreate == "object" ?
15285                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15286         if(this.id && !cfg.id){
15287             cfg.id = this.id;
15288         }
15289         return cfg;
15290     },
15291
15292     /** @private */
15293     afterRender : Roo.emptyFn,
15294
15295     /**
15296      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15297      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15298      */
15299     destroy : function(){
15300         if(this.fireEvent("beforedestroy", this) !== false){
15301             this.purgeListeners();
15302             this.beforeDestroy();
15303             if(this.rendered){
15304                 this.el.removeAllListeners();
15305                 this.el.remove();
15306                 if(this.actionMode == "container"){
15307                     this.container.remove();
15308                 }
15309             }
15310             this.onDestroy();
15311             Roo.ComponentMgr.unregister(this);
15312             this.fireEvent("destroy", this);
15313         }
15314     },
15315
15316         /** @private */
15317     beforeDestroy : function(){
15318
15319     },
15320
15321         /** @private */
15322         onDestroy : function(){
15323
15324     },
15325
15326     /**
15327      * Returns the underlying {@link Roo.Element}.
15328      * @return {Roo.Element} The element
15329      */
15330     getEl : function(){
15331         return this.el;
15332     },
15333
15334     /**
15335      * Returns the id of this component.
15336      * @return {String}
15337      */
15338     getId : function(){
15339         return this.id;
15340     },
15341
15342     /**
15343      * Try to focus this component.
15344      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15345      * @return {Roo.Component} this
15346      */
15347     focus : function(selectText){
15348         if(this.rendered){
15349             this.el.focus();
15350             if(selectText === true){
15351                 this.el.dom.select();
15352             }
15353         }
15354         return this;
15355     },
15356
15357     /** @private */
15358     blur : function(){
15359         if(this.rendered){
15360             this.el.blur();
15361         }
15362         return this;
15363     },
15364
15365     /**
15366      * Disable this component.
15367      * @return {Roo.Component} this
15368      */
15369     disable : function(){
15370         if(this.rendered){
15371             this.onDisable();
15372         }
15373         this.disabled = true;
15374         this.fireEvent("disable", this);
15375         return this;
15376     },
15377
15378         // private
15379     onDisable : function(){
15380         this.getActionEl().addClass(this.disabledClass);
15381         this.el.dom.disabled = true;
15382     },
15383
15384     /**
15385      * Enable this component.
15386      * @return {Roo.Component} this
15387      */
15388     enable : function(){
15389         if(this.rendered){
15390             this.onEnable();
15391         }
15392         this.disabled = false;
15393         this.fireEvent("enable", this);
15394         return this;
15395     },
15396
15397         // private
15398     onEnable : function(){
15399         this.getActionEl().removeClass(this.disabledClass);
15400         this.el.dom.disabled = false;
15401     },
15402
15403     /**
15404      * Convenience function for setting disabled/enabled by boolean.
15405      * @param {Boolean} disabled
15406      */
15407     setDisabled : function(disabled){
15408         this[disabled ? "disable" : "enable"]();
15409     },
15410
15411     /**
15412      * Show this component.
15413      * @return {Roo.Component} this
15414      */
15415     show: function(){
15416         if(this.fireEvent("beforeshow", this) !== false){
15417             this.hidden = false;
15418             if(this.rendered){
15419                 this.onShow();
15420             }
15421             this.fireEvent("show", this);
15422         }
15423         return this;
15424     },
15425
15426     // private
15427     onShow : function(){
15428         var ae = this.getActionEl();
15429         if(this.hideMode == 'visibility'){
15430             ae.dom.style.visibility = "visible";
15431         }else if(this.hideMode == 'offsets'){
15432             ae.removeClass('x-hidden');
15433         }else{
15434             ae.dom.style.display = "";
15435         }
15436     },
15437
15438     /**
15439      * Hide this component.
15440      * @return {Roo.Component} this
15441      */
15442     hide: function(){
15443         if(this.fireEvent("beforehide", this) !== false){
15444             this.hidden = true;
15445             if(this.rendered){
15446                 this.onHide();
15447             }
15448             this.fireEvent("hide", this);
15449         }
15450         return this;
15451     },
15452
15453     // private
15454     onHide : function(){
15455         var ae = this.getActionEl();
15456         if(this.hideMode == 'visibility'){
15457             ae.dom.style.visibility = "hidden";
15458         }else if(this.hideMode == 'offsets'){
15459             ae.addClass('x-hidden');
15460         }else{
15461             ae.dom.style.display = "none";
15462         }
15463     },
15464
15465     /**
15466      * Convenience function to hide or show this component by boolean.
15467      * @param {Boolean} visible True to show, false to hide
15468      * @return {Roo.Component} this
15469      */
15470     setVisible: function(visible){
15471         if(visible) {
15472             this.show();
15473         }else{
15474             this.hide();
15475         }
15476         return this;
15477     },
15478
15479     /**
15480      * Returns true if this component is visible.
15481      */
15482     isVisible : function(){
15483         return this.getActionEl().isVisible();
15484     },
15485
15486     cloneConfig : function(overrides){
15487         overrides = overrides || {};
15488         var id = overrides.id || Roo.id();
15489         var cfg = Roo.applyIf(overrides, this.initialConfig);
15490         cfg.id = id; // prevent dup id
15491         return new this.constructor(cfg);
15492     }
15493 });/*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503
15504 /**
15505  * @class Roo.BoxComponent
15506  * @extends Roo.Component
15507  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15508  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15509  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15510  * layout containers.
15511  * @constructor
15512  * @param {Roo.Element/String/Object} config The configuration options.
15513  */
15514 Roo.BoxComponent = function(config){
15515     Roo.Component.call(this, config);
15516     this.addEvents({
15517         /**
15518          * @event resize
15519          * Fires after the component is resized.
15520              * @param {Roo.Component} this
15521              * @param {Number} adjWidth The box-adjusted width that was set
15522              * @param {Number} adjHeight The box-adjusted height that was set
15523              * @param {Number} rawWidth The width that was originally specified
15524              * @param {Number} rawHeight The height that was originally specified
15525              */
15526         resize : true,
15527         /**
15528          * @event move
15529          * Fires after the component is moved.
15530              * @param {Roo.Component} this
15531              * @param {Number} x The new x position
15532              * @param {Number} y The new y position
15533              */
15534         move : true
15535     });
15536 };
15537
15538 Roo.extend(Roo.BoxComponent, Roo.Component, {
15539     // private, set in afterRender to signify that the component has been rendered
15540     boxReady : false,
15541     // private, used to defer height settings to subclasses
15542     deferHeight: false,
15543     /** @cfg {Number} width
15544      * width (optional) size of component
15545      */
15546      /** @cfg {Number} height
15547      * height (optional) size of component
15548      */
15549      
15550     /**
15551      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15552      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15553      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15554      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15555      * @return {Roo.BoxComponent} this
15556      */
15557     setSize : function(w, h){
15558         // support for standard size objects
15559         if(typeof w == 'object'){
15560             h = w.height;
15561             w = w.width;
15562         }
15563         // not rendered
15564         if(!this.boxReady){
15565             this.width = w;
15566             this.height = h;
15567             return this;
15568         }
15569
15570         // prevent recalcs when not needed
15571         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15572             return this;
15573         }
15574         this.lastSize = {width: w, height: h};
15575
15576         var adj = this.adjustSize(w, h);
15577         var aw = adj.width, ah = adj.height;
15578         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15579             var rz = this.getResizeEl();
15580             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15581                 rz.setSize(aw, ah);
15582             }else if(!this.deferHeight && ah !== undefined){
15583                 rz.setHeight(ah);
15584             }else if(aw !== undefined){
15585                 rz.setWidth(aw);
15586             }
15587             this.onResize(aw, ah, w, h);
15588             this.fireEvent('resize', this, aw, ah, w, h);
15589         }
15590         return this;
15591     },
15592
15593     /**
15594      * Gets the current size of the component's underlying element.
15595      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15596      */
15597     getSize : function(){
15598         return this.el.getSize();
15599     },
15600
15601     /**
15602      * Gets the current XY position of the component's underlying element.
15603      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15604      * @return {Array} The XY position of the element (e.g., [100, 200])
15605      */
15606     getPosition : function(local){
15607         if(local === true){
15608             return [this.el.getLeft(true), this.el.getTop(true)];
15609         }
15610         return this.xy || this.el.getXY();
15611     },
15612
15613     /**
15614      * Gets the current box measurements of the component's underlying element.
15615      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15616      * @returns {Object} box An object in the format {x, y, width, height}
15617      */
15618     getBox : function(local){
15619         var s = this.el.getSize();
15620         if(local){
15621             s.x = this.el.getLeft(true);
15622             s.y = this.el.getTop(true);
15623         }else{
15624             var xy = this.xy || this.el.getXY();
15625             s.x = xy[0];
15626             s.y = xy[1];
15627         }
15628         return s;
15629     },
15630
15631     /**
15632      * Sets the current box measurements of the component's underlying element.
15633      * @param {Object} box An object in the format {x, y, width, height}
15634      * @returns {Roo.BoxComponent} this
15635      */
15636     updateBox : function(box){
15637         this.setSize(box.width, box.height);
15638         this.setPagePosition(box.x, box.y);
15639         return this;
15640     },
15641
15642     // protected
15643     getResizeEl : function(){
15644         return this.resizeEl || this.el;
15645     },
15646
15647     // protected
15648     getPositionEl : function(){
15649         return this.positionEl || this.el;
15650     },
15651
15652     /**
15653      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15654      * This method fires the move event.
15655      * @param {Number} left The new left
15656      * @param {Number} top The new top
15657      * @returns {Roo.BoxComponent} this
15658      */
15659     setPosition : function(x, y){
15660         this.x = x;
15661         this.y = y;
15662         if(!this.boxReady){
15663             return this;
15664         }
15665         var adj = this.adjustPosition(x, y);
15666         var ax = adj.x, ay = adj.y;
15667
15668         var el = this.getPositionEl();
15669         if(ax !== undefined || ay !== undefined){
15670             if(ax !== undefined && ay !== undefined){
15671                 el.setLeftTop(ax, ay);
15672             }else if(ax !== undefined){
15673                 el.setLeft(ax);
15674             }else if(ay !== undefined){
15675                 el.setTop(ay);
15676             }
15677             this.onPosition(ax, ay);
15678             this.fireEvent('move', this, ax, ay);
15679         }
15680         return this;
15681     },
15682
15683     /**
15684      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15685      * This method fires the move event.
15686      * @param {Number} x The new x position
15687      * @param {Number} y The new y position
15688      * @returns {Roo.BoxComponent} this
15689      */
15690     setPagePosition : function(x, y){
15691         this.pageX = x;
15692         this.pageY = y;
15693         if(!this.boxReady){
15694             return;
15695         }
15696         if(x === undefined || y === undefined){ // cannot translate undefined points
15697             return;
15698         }
15699         var p = this.el.translatePoints(x, y);
15700         this.setPosition(p.left, p.top);
15701         return this;
15702     },
15703
15704     // private
15705     onRender : function(ct, position){
15706         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15707         if(this.resizeEl){
15708             this.resizeEl = Roo.get(this.resizeEl);
15709         }
15710         if(this.positionEl){
15711             this.positionEl = Roo.get(this.positionEl);
15712         }
15713     },
15714
15715     // private
15716     afterRender : function(){
15717         Roo.BoxComponent.superclass.afterRender.call(this);
15718         this.boxReady = true;
15719         this.setSize(this.width, this.height);
15720         if(this.x || this.y){
15721             this.setPosition(this.x, this.y);
15722         }
15723         if(this.pageX || this.pageY){
15724             this.setPagePosition(this.pageX, this.pageY);
15725         }
15726     },
15727
15728     /**
15729      * Force the component's size to recalculate based on the underlying element's current height and width.
15730      * @returns {Roo.BoxComponent} this
15731      */
15732     syncSize : function(){
15733         delete this.lastSize;
15734         this.setSize(this.el.getWidth(), this.el.getHeight());
15735         return this;
15736     },
15737
15738     /**
15739      * Called after the component is resized, this method is empty by default but can be implemented by any
15740      * subclass that needs to perform custom logic after a resize occurs.
15741      * @param {Number} adjWidth The box-adjusted width that was set
15742      * @param {Number} adjHeight The box-adjusted height that was set
15743      * @param {Number} rawWidth The width that was originally specified
15744      * @param {Number} rawHeight The height that was originally specified
15745      */
15746     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15747
15748     },
15749
15750     /**
15751      * Called after the component is moved, this method is empty by default but can be implemented by any
15752      * subclass that needs to perform custom logic after a move occurs.
15753      * @param {Number} x The new x position
15754      * @param {Number} y The new y position
15755      */
15756     onPosition : function(x, y){
15757
15758     },
15759
15760     // private
15761     adjustSize : function(w, h){
15762         if(this.autoWidth){
15763             w = 'auto';
15764         }
15765         if(this.autoHeight){
15766             h = 'auto';
15767         }
15768         return {width : w, height: h};
15769     },
15770
15771     // private
15772     adjustPosition : function(x, y){
15773         return {x : x, y: y};
15774     }
15775 });/*
15776  * Original code for Roojs - LGPL
15777  * <script type="text/javascript">
15778  */
15779  
15780 /**
15781  * @class Roo.XComponent
15782  * A delayed Element creator...
15783  * Or a way to group chunks of interface together.
15784  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15785  *  used in conjunction with XComponent.build() it will create an instance of each element,
15786  *  then call addxtype() to build the User interface.
15787  * 
15788  * Mypart.xyx = new Roo.XComponent({
15789
15790     parent : 'Mypart.xyz', // empty == document.element.!!
15791     order : '001',
15792     name : 'xxxx'
15793     region : 'xxxx'
15794     disabled : function() {} 
15795      
15796     tree : function() { // return an tree of xtype declared components
15797         var MODULE = this;
15798         return 
15799         {
15800             xtype : 'NestedLayoutPanel',
15801             // technicall
15802         }
15803      ]
15804  *})
15805  *
15806  *
15807  * It can be used to build a big heiracy, with parent etc.
15808  * or you can just use this to render a single compoent to a dom element
15809  * MYPART.render(Roo.Element | String(id) | dom_element )
15810  *
15811  *
15812  * Usage patterns.
15813  *
15814  * Classic Roo
15815  *
15816  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15817  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15818  *
15819  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15820  *
15821  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15822  * - if mulitple topModules exist, the last one is defined as the top module.
15823  *
15824  * Embeded Roo
15825  * 
15826  * When the top level or multiple modules are to embedded into a existing HTML page,
15827  * the parent element can container '#id' of the element where the module will be drawn.
15828  *
15829  * Bootstrap Roo
15830  *
15831  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15832  * it relies more on a include mechanism, where sub modules are included into an outer page.
15833  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15834  * 
15835  * Bootstrap Roo Included elements
15836  *
15837  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15838  * hence confusing the component builder as it thinks there are multiple top level elements. 
15839  *
15840  * 
15841  * 
15842  * @extends Roo.util.Observable
15843  * @constructor
15844  * @param cfg {Object} configuration of component
15845  * 
15846  */
15847 Roo.XComponent = function(cfg) {
15848     Roo.apply(this, cfg);
15849     this.addEvents({ 
15850         /**
15851              * @event built
15852              * Fires when this the componnt is built
15853              * @param {Roo.XComponent} c the component
15854              */
15855         'built' : true
15856         
15857     });
15858     this.region = this.region || 'center'; // default..
15859     Roo.XComponent.register(this);
15860     this.modules = false;
15861     this.el = false; // where the layout goes..
15862     
15863     
15864 }
15865 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15866     /**
15867      * @property el
15868      * The created element (with Roo.factory())
15869      * @type {Roo.Layout}
15870      */
15871     el  : false,
15872     
15873     /**
15874      * @property el
15875      * for BC  - use el in new code
15876      * @type {Roo.Layout}
15877      */
15878     panel : false,
15879     
15880     /**
15881      * @property layout
15882      * for BC  - use el in new code
15883      * @type {Roo.Layout}
15884      */
15885     layout : false,
15886     
15887      /**
15888      * @cfg {Function|boolean} disabled
15889      * If this module is disabled by some rule, return true from the funtion
15890      */
15891     disabled : false,
15892     
15893     /**
15894      * @cfg {String} parent 
15895      * Name of parent element which it get xtype added to..
15896      */
15897     parent: false,
15898     
15899     /**
15900      * @cfg {String} order
15901      * Used to set the order in which elements are created (usefull for multiple tabs)
15902      */
15903     
15904     order : false,
15905     /**
15906      * @cfg {String} name
15907      * String to display while loading.
15908      */
15909     name : false,
15910     /**
15911      * @cfg {String} region
15912      * Region to render component to (defaults to center)
15913      */
15914     region : 'center',
15915     
15916     /**
15917      * @cfg {Array} items
15918      * A single item array - the first element is the root of the tree..
15919      * It's done this way to stay compatible with the Xtype system...
15920      */
15921     items : false,
15922     
15923     /**
15924      * @property _tree
15925      * The method that retuns the tree of parts that make up this compoennt 
15926      * @type {function}
15927      */
15928     _tree  : false,
15929     
15930      /**
15931      * render
15932      * render element to dom or tree
15933      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15934      */
15935     
15936     render : function(el)
15937     {
15938         
15939         el = el || false;
15940         var hp = this.parent ? 1 : 0;
15941         Roo.debug &&  Roo.log(this);
15942         
15943         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15944             // if parent is a '#.....' string, then let's use that..
15945             var ename = this.parent.substr(1);
15946             this.parent = false;
15947             Roo.debug && Roo.log(ename);
15948             switch (ename) {
15949                 case 'bootstrap-body' :
15950                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15951                         this.parent = { el :  new  Roo.bootstrap.Body() };
15952                         Roo.debug && Roo.log("setting el to doc body");
15953                          
15954                     } else {
15955                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15956                     }
15957                     break;
15958                 case 'bootstrap':
15959                     this.parent = { el : true};
15960                     // fall through
15961                 default:
15962                     el = Roo.get(ename);
15963                     break;
15964             }
15965                 
15966             
15967             if (!el && !this.parent) {
15968                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15969                 return;
15970             }
15971         }
15972         Roo.debug && Roo.log("EL:");
15973         Roo.debug && Roo.log(el);
15974         Roo.debug && Roo.log("this.parent.el:");
15975         Roo.debug && Roo.log(this.parent.el);
15976         
15977         var tree = this._tree ? this._tree() : this.tree();
15978
15979         // altertive root elements ??? - we need a better way to indicate these.
15980         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15981                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15982         
15983         if (!this.parent && is_alt) {
15984             //el = Roo.get(document.body);
15985             this.parent = { el : true };
15986         }
15987             
15988             
15989         
15990         if (!this.parent) {
15991             
15992             Roo.debug && Roo.log("no parent - creating one");
15993             
15994             el = el ? Roo.get(el) : false;      
15995             
15996             // it's a top level one..
15997             this.parent =  {
15998                 el : new Roo.BorderLayout(el || document.body, {
15999                 
16000                      center: {
16001                          titlebar: false,
16002                          autoScroll:false,
16003                          closeOnTab: true,
16004                          tabPosition: 'top',
16005                           //resizeTabs: true,
16006                          alwaysShowTabs: el && hp? false :  true,
16007                          hideTabs: el || !hp ? true :  false,
16008                          minTabWidth: 140
16009                      }
16010                  })
16011             }
16012         }
16013         
16014         if (!this.parent.el) {
16015                 // probably an old style ctor, which has been disabled.
16016                 return;
16017
16018         }
16019                 // The 'tree' method is  '_tree now' 
16020             
16021         tree.region = tree.region || this.region;
16022         
16023         if (this.parent.el === true) {
16024             // bootstrap... - body..
16025             this.parent.el = Roo.factory(tree);
16026         }
16027         
16028         this.el = this.parent.el.addxtype(tree);
16029         this.fireEvent('built', this);
16030         
16031         this.panel = this.el;
16032         this.layout = this.panel.layout;
16033         this.parentLayout = this.parent.layout  || false;  
16034          
16035     }
16036     
16037 });
16038
16039 Roo.apply(Roo.XComponent, {
16040     /**
16041      * @property  hideProgress
16042      * true to disable the building progress bar.. usefull on single page renders.
16043      * @type Boolean
16044      */
16045     hideProgress : false,
16046     /**
16047      * @property  buildCompleted
16048      * True when the builder has completed building the interface.
16049      * @type Boolean
16050      */
16051     buildCompleted : false,
16052      
16053     /**
16054      * @property  topModule
16055      * the upper most module - uses document.element as it's constructor.
16056      * @type Object
16057      */
16058      
16059     topModule  : false,
16060       
16061     /**
16062      * @property  modules
16063      * array of modules to be created by registration system.
16064      * @type {Array} of Roo.XComponent
16065      */
16066     
16067     modules : [],
16068     /**
16069      * @property  elmodules
16070      * array of modules to be created by which use #ID 
16071      * @type {Array} of Roo.XComponent
16072      */
16073      
16074     elmodules : [],
16075
16076      /**
16077      * @property  build_from_html
16078      * Build elements from html - used by bootstrap HTML stuff 
16079      *    - this is cleared after build is completed
16080      * @type {boolean} true  (default false)
16081      */
16082      
16083     build_from_html : false,
16084
16085     /**
16086      * Register components to be built later.
16087      *
16088      * This solves the following issues
16089      * - Building is not done on page load, but after an authentication process has occured.
16090      * - Interface elements are registered on page load
16091      * - Parent Interface elements may not be loaded before child, so this handles that..
16092      * 
16093      *
16094      * example:
16095      * 
16096      * MyApp.register({
16097           order : '000001',
16098           module : 'Pman.Tab.projectMgr',
16099           region : 'center',
16100           parent : 'Pman.layout',
16101           disabled : false,  // or use a function..
16102         })
16103      
16104      * * @param {Object} details about module
16105      */
16106     register : function(obj) {
16107                 
16108         Roo.XComponent.event.fireEvent('register', obj);
16109         switch(typeof(obj.disabled) ) {
16110                 
16111             case 'undefined':
16112                 break;
16113             
16114             case 'function':
16115                 if ( obj.disabled() ) {
16116                         return;
16117                 }
16118                 break;
16119             
16120             default:
16121                 if (obj.disabled) {
16122                         return;
16123                 }
16124                 break;
16125         }
16126                 
16127         this.modules.push(obj);
16128          
16129     },
16130     /**
16131      * convert a string to an object..
16132      * eg. 'AAA.BBB' -> finds AAA.BBB
16133
16134      */
16135     
16136     toObject : function(str)
16137     {
16138         if (!str || typeof(str) == 'object') {
16139             return str;
16140         }
16141         if (str.substring(0,1) == '#') {
16142             return str;
16143         }
16144
16145         var ar = str.split('.');
16146         var rt, o;
16147         rt = ar.shift();
16148             /** eval:var:o */
16149         try {
16150             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16151         } catch (e) {
16152             throw "Module not found : " + str;
16153         }
16154         
16155         if (o === false) {
16156             throw "Module not found : " + str;
16157         }
16158         Roo.each(ar, function(e) {
16159             if (typeof(o[e]) == 'undefined') {
16160                 throw "Module not found : " + str;
16161             }
16162             o = o[e];
16163         });
16164         
16165         return o;
16166         
16167     },
16168     
16169     
16170     /**
16171      * move modules into their correct place in the tree..
16172      * 
16173      */
16174     preBuild : function ()
16175     {
16176         var _t = this;
16177         Roo.each(this.modules , function (obj)
16178         {
16179             Roo.XComponent.event.fireEvent('beforebuild', obj);
16180             
16181             var opar = obj.parent;
16182             try { 
16183                 obj.parent = this.toObject(opar);
16184             } catch(e) {
16185                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16186                 return;
16187             }
16188             
16189             if (!obj.parent) {
16190                 Roo.debug && Roo.log("GOT top level module");
16191                 Roo.debug && Roo.log(obj);
16192                 obj.modules = new Roo.util.MixedCollection(false, 
16193                     function(o) { return o.order + '' }
16194                 );
16195                 this.topModule = obj;
16196                 return;
16197             }
16198                         // parent is a string (usually a dom element name..)
16199             if (typeof(obj.parent) == 'string') {
16200                 this.elmodules.push(obj);
16201                 return;
16202             }
16203             if (obj.parent.constructor != Roo.XComponent) {
16204                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16205             }
16206             if (!obj.parent.modules) {
16207                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16208                     function(o) { return o.order + '' }
16209                 );
16210             }
16211             if (obj.parent.disabled) {
16212                 obj.disabled = true;
16213             }
16214             obj.parent.modules.add(obj);
16215         }, this);
16216     },
16217     
16218      /**
16219      * make a list of modules to build.
16220      * @return {Array} list of modules. 
16221      */ 
16222     
16223     buildOrder : function()
16224     {
16225         var _this = this;
16226         var cmp = function(a,b) {   
16227             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16228         };
16229         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16230             throw "No top level modules to build";
16231         }
16232         
16233         // make a flat list in order of modules to build.
16234         var mods = this.topModule ? [ this.topModule ] : [];
16235                 
16236         
16237         // elmodules (is a list of DOM based modules )
16238         Roo.each(this.elmodules, function(e) {
16239             mods.push(e);
16240             if (!this.topModule &&
16241                 typeof(e.parent) == 'string' &&
16242                 e.parent.substring(0,1) == '#' &&
16243                 Roo.get(e.parent.substr(1))
16244                ) {
16245                 
16246                 _this.topModule = e;
16247             }
16248             
16249         });
16250
16251         
16252         // add modules to their parents..
16253         var addMod = function(m) {
16254             Roo.debug && Roo.log("build Order: add: " + m.name);
16255                 
16256             mods.push(m);
16257             if (m.modules && !m.disabled) {
16258                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16259                 m.modules.keySort('ASC',  cmp );
16260                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16261     
16262                 m.modules.each(addMod);
16263             } else {
16264                 Roo.debug && Roo.log("build Order: no child modules");
16265             }
16266             // not sure if this is used any more..
16267             if (m.finalize) {
16268                 m.finalize.name = m.name + " (clean up) ";
16269                 mods.push(m.finalize);
16270             }
16271             
16272         }
16273         if (this.topModule && this.topModule.modules) { 
16274             this.topModule.modules.keySort('ASC',  cmp );
16275             this.topModule.modules.each(addMod);
16276         } 
16277         return mods;
16278     },
16279     
16280      /**
16281      * Build the registered modules.
16282      * @param {Object} parent element.
16283      * @param {Function} optional method to call after module has been added.
16284      * 
16285      */ 
16286    
16287     build : function(opts) 
16288     {
16289         
16290         if (typeof(opts) != 'undefined') {
16291             Roo.apply(this,opts);
16292         }
16293         
16294         this.preBuild();
16295         var mods = this.buildOrder();
16296       
16297         //this.allmods = mods;
16298         //Roo.debug && Roo.log(mods);
16299         //return;
16300         if (!mods.length) { // should not happen
16301             throw "NO modules!!!";
16302         }
16303         
16304         
16305         var msg = "Building Interface...";
16306         // flash it up as modal - so we store the mask!?
16307         if (!this.hideProgress && Roo.MessageBox) {
16308             Roo.MessageBox.show({ title: 'loading' });
16309             Roo.MessageBox.show({
16310                title: "Please wait...",
16311                msg: msg,
16312                width:450,
16313                progress:true,
16314                closable:false,
16315                modal: false
16316               
16317             });
16318         }
16319         var total = mods.length;
16320         
16321         var _this = this;
16322         var progressRun = function() {
16323             if (!mods.length) {
16324                 Roo.debug && Roo.log('hide?');
16325                 if (!this.hideProgress && Roo.MessageBox) {
16326                     Roo.MessageBox.hide();
16327                 }
16328                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16329                 
16330                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16331                 
16332                 // THE END...
16333                 return false;   
16334             }
16335             
16336             var m = mods.shift();
16337             
16338             
16339             Roo.debug && Roo.log(m);
16340             // not sure if this is supported any more.. - modules that are are just function
16341             if (typeof(m) == 'function') { 
16342                 m.call(this);
16343                 return progressRun.defer(10, _this);
16344             } 
16345             
16346             
16347             msg = "Building Interface " + (total  - mods.length) + 
16348                     " of " + total + 
16349                     (m.name ? (' - ' + m.name) : '');
16350                         Roo.debug && Roo.log(msg);
16351             if (!this.hideProgress &&  Roo.MessageBox) { 
16352                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16353             }
16354             
16355          
16356             // is the module disabled?
16357             var disabled = (typeof(m.disabled) == 'function') ?
16358                 m.disabled.call(m.module.disabled) : m.disabled;    
16359             
16360             
16361             if (disabled) {
16362                 return progressRun(); // we do not update the display!
16363             }
16364             
16365             // now build 
16366             
16367                         
16368                         
16369             m.render();
16370             // it's 10 on top level, and 1 on others??? why...
16371             return progressRun.defer(10, _this);
16372              
16373         }
16374         progressRun.defer(1, _this);
16375      
16376         
16377         
16378     },
16379         
16380         
16381         /**
16382          * Event Object.
16383          *
16384          *
16385          */
16386         event: false, 
16387     /**
16388          * wrapper for event.on - aliased later..  
16389          * Typically use to register a event handler for register:
16390          *
16391          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16392          *
16393          */
16394     on : false
16395    
16396     
16397     
16398 });
16399
16400 Roo.XComponent.event = new Roo.util.Observable({
16401                 events : { 
16402                         /**
16403                          * @event register
16404                          * Fires when an Component is registered,
16405                          * set the disable property on the Component to stop registration.
16406                          * @param {Roo.XComponent} c the component being registerd.
16407                          * 
16408                          */
16409                         'register' : true,
16410             /**
16411                          * @event beforebuild
16412                          * Fires before each Component is built
16413                          * can be used to apply permissions.
16414                          * @param {Roo.XComponent} c the component being registerd.
16415                          * 
16416                          */
16417                         'beforebuild' : true,
16418                         /**
16419                          * @event buildcomplete
16420                          * Fires on the top level element when all elements have been built
16421                          * @param {Roo.XComponent} the top level component.
16422                          */
16423                         'buildcomplete' : true
16424                         
16425                 }
16426 });
16427
16428 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16429  /*
16430  * Based on:
16431  * Ext JS Library 1.1.1
16432  * Copyright(c) 2006-2007, Ext JS, LLC.
16433  *
16434  * Originally Released Under LGPL - original licence link has changed is not relivant.
16435  *
16436  * Fork - LGPL
16437  * <script type="text/javascript">
16438  */
16439
16440
16441
16442 /*
16443  * These classes are derivatives of the similarly named classes in the YUI Library.
16444  * The original license:
16445  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16446  * Code licensed under the BSD License:
16447  * http://developer.yahoo.net/yui/license.txt
16448  */
16449
16450 (function() {
16451
16452 var Event=Roo.EventManager;
16453 var Dom=Roo.lib.Dom;
16454
16455 /**
16456  * @class Roo.dd.DragDrop
16457  * @extends Roo.util.Observable
16458  * Defines the interface and base operation of items that that can be
16459  * dragged or can be drop targets.  It was designed to be extended, overriding
16460  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16461  * Up to three html elements can be associated with a DragDrop instance:
16462  * <ul>
16463  * <li>linked element: the element that is passed into the constructor.
16464  * This is the element which defines the boundaries for interaction with
16465  * other DragDrop objects.</li>
16466  * <li>handle element(s): The drag operation only occurs if the element that
16467  * was clicked matches a handle element.  By default this is the linked
16468  * element, but there are times that you will want only a portion of the
16469  * linked element to initiate the drag operation, and the setHandleElId()
16470  * method provides a way to define this.</li>
16471  * <li>drag element: this represents the element that would be moved along
16472  * with the cursor during a drag operation.  By default, this is the linked
16473  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16474  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16475  * </li>
16476  * </ul>
16477  * This class should not be instantiated until the onload event to ensure that
16478  * the associated elements are available.
16479  * The following would define a DragDrop obj that would interact with any
16480  * other DragDrop obj in the "group1" group:
16481  * <pre>
16482  *  dd = new Roo.dd.DragDrop("div1", "group1");
16483  * </pre>
16484  * Since none of the event handlers have been implemented, nothing would
16485  * actually happen if you were to run the code above.  Normally you would
16486  * override this class or one of the default implementations, but you can
16487  * also override the methods you want on an instance of the class...
16488  * <pre>
16489  *  dd.onDragDrop = function(e, id) {
16490  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16491  *  }
16492  * </pre>
16493  * @constructor
16494  * @param {String} id of the element that is linked to this instance
16495  * @param {String} sGroup the group of related DragDrop objects
16496  * @param {object} config an object containing configurable attributes
16497  *                Valid properties for DragDrop:
16498  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16499  */
16500 Roo.dd.DragDrop = function(id, sGroup, config) {
16501     if (id) {
16502         this.init(id, sGroup, config);
16503     }
16504     
16505 };
16506
16507 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16508
16509     /**
16510      * The id of the element associated with this object.  This is what we
16511      * refer to as the "linked element" because the size and position of
16512      * this element is used to determine when the drag and drop objects have
16513      * interacted.
16514      * @property id
16515      * @type String
16516      */
16517     id: null,
16518
16519     /**
16520      * Configuration attributes passed into the constructor
16521      * @property config
16522      * @type object
16523      */
16524     config: null,
16525
16526     /**
16527      * The id of the element that will be dragged.  By default this is same
16528      * as the linked element , but could be changed to another element. Ex:
16529      * Roo.dd.DDProxy
16530      * @property dragElId
16531      * @type String
16532      * @private
16533      */
16534     dragElId: null,
16535
16536     /**
16537      * the id of the element that initiates the drag operation.  By default
16538      * this is the linked element, but could be changed to be a child of this
16539      * element.  This lets us do things like only starting the drag when the
16540      * header element within the linked html element is clicked.
16541      * @property handleElId
16542      * @type String
16543      * @private
16544      */
16545     handleElId: null,
16546
16547     /**
16548      * An associative array of HTML tags that will be ignored if clicked.
16549      * @property invalidHandleTypes
16550      * @type {string: string}
16551      */
16552     invalidHandleTypes: null,
16553
16554     /**
16555      * An associative array of ids for elements that will be ignored if clicked
16556      * @property invalidHandleIds
16557      * @type {string: string}
16558      */
16559     invalidHandleIds: null,
16560
16561     /**
16562      * An indexted array of css class names for elements that will be ignored
16563      * if clicked.
16564      * @property invalidHandleClasses
16565      * @type string[]
16566      */
16567     invalidHandleClasses: null,
16568
16569     /**
16570      * The linked element's absolute X position at the time the drag was
16571      * started
16572      * @property startPageX
16573      * @type int
16574      * @private
16575      */
16576     startPageX: 0,
16577
16578     /**
16579      * The linked element's absolute X position at the time the drag was
16580      * started
16581      * @property startPageY
16582      * @type int
16583      * @private
16584      */
16585     startPageY: 0,
16586
16587     /**
16588      * The group defines a logical collection of DragDrop objects that are
16589      * related.  Instances only get events when interacting with other
16590      * DragDrop object in the same group.  This lets us define multiple
16591      * groups using a single DragDrop subclass if we want.
16592      * @property groups
16593      * @type {string: string}
16594      */
16595     groups: null,
16596
16597     /**
16598      * Individual drag/drop instances can be locked.  This will prevent
16599      * onmousedown start drag.
16600      * @property locked
16601      * @type boolean
16602      * @private
16603      */
16604     locked: false,
16605
16606     /**
16607      * Lock this instance
16608      * @method lock
16609      */
16610     lock: function() { this.locked = true; },
16611
16612     /**
16613      * Unlock this instace
16614      * @method unlock
16615      */
16616     unlock: function() { this.locked = false; },
16617
16618     /**
16619      * By default, all insances can be a drop target.  This can be disabled by
16620      * setting isTarget to false.
16621      * @method isTarget
16622      * @type boolean
16623      */
16624     isTarget: true,
16625
16626     /**
16627      * The padding configured for this drag and drop object for calculating
16628      * the drop zone intersection with this object.
16629      * @method padding
16630      * @type int[]
16631      */
16632     padding: null,
16633
16634     /**
16635      * Cached reference to the linked element
16636      * @property _domRef
16637      * @private
16638      */
16639     _domRef: null,
16640
16641     /**
16642      * Internal typeof flag
16643      * @property __ygDragDrop
16644      * @private
16645      */
16646     __ygDragDrop: true,
16647
16648     /**
16649      * Set to true when horizontal contraints are applied
16650      * @property constrainX
16651      * @type boolean
16652      * @private
16653      */
16654     constrainX: false,
16655
16656     /**
16657      * Set to true when vertical contraints are applied
16658      * @property constrainY
16659      * @type boolean
16660      * @private
16661      */
16662     constrainY: false,
16663
16664     /**
16665      * The left constraint
16666      * @property minX
16667      * @type int
16668      * @private
16669      */
16670     minX: 0,
16671
16672     /**
16673      * The right constraint
16674      * @property maxX
16675      * @type int
16676      * @private
16677      */
16678     maxX: 0,
16679
16680     /**
16681      * The up constraint
16682      * @property minY
16683      * @type int
16684      * @type int
16685      * @private
16686      */
16687     minY: 0,
16688
16689     /**
16690      * The down constraint
16691      * @property maxY
16692      * @type int
16693      * @private
16694      */
16695     maxY: 0,
16696
16697     /**
16698      * Maintain offsets when we resetconstraints.  Set to true when you want
16699      * the position of the element relative to its parent to stay the same
16700      * when the page changes
16701      *
16702      * @property maintainOffset
16703      * @type boolean
16704      */
16705     maintainOffset: false,
16706
16707     /**
16708      * Array of pixel locations the element will snap to if we specified a
16709      * horizontal graduation/interval.  This array is generated automatically
16710      * when you define a tick interval.
16711      * @property xTicks
16712      * @type int[]
16713      */
16714     xTicks: null,
16715
16716     /**
16717      * Array of pixel locations the element will snap to if we specified a
16718      * vertical graduation/interval.  This array is generated automatically
16719      * when you define a tick interval.
16720      * @property yTicks
16721      * @type int[]
16722      */
16723     yTicks: null,
16724
16725     /**
16726      * By default the drag and drop instance will only respond to the primary
16727      * button click (left button for a right-handed mouse).  Set to true to
16728      * allow drag and drop to start with any mouse click that is propogated
16729      * by the browser
16730      * @property primaryButtonOnly
16731      * @type boolean
16732      */
16733     primaryButtonOnly: true,
16734
16735     /**
16736      * The availabe property is false until the linked dom element is accessible.
16737      * @property available
16738      * @type boolean
16739      */
16740     available: false,
16741
16742     /**
16743      * By default, drags can only be initiated if the mousedown occurs in the
16744      * region the linked element is.  This is done in part to work around a
16745      * bug in some browsers that mis-report the mousedown if the previous
16746      * mouseup happened outside of the window.  This property is set to true
16747      * if outer handles are defined.
16748      *
16749      * @property hasOuterHandles
16750      * @type boolean
16751      * @default false
16752      */
16753     hasOuterHandles: false,
16754
16755     /**
16756      * Code that executes immediately before the startDrag event
16757      * @method b4StartDrag
16758      * @private
16759      */
16760     b4StartDrag: function(x, y) { },
16761
16762     /**
16763      * Abstract method called after a drag/drop object is clicked
16764      * and the drag or mousedown time thresholds have beeen met.
16765      * @method startDrag
16766      * @param {int} X click location
16767      * @param {int} Y click location
16768      */
16769     startDrag: function(x, y) { /* override this */ },
16770
16771     /**
16772      * Code that executes immediately before the onDrag event
16773      * @method b4Drag
16774      * @private
16775      */
16776     b4Drag: function(e) { },
16777
16778     /**
16779      * Abstract method called during the onMouseMove event while dragging an
16780      * object.
16781      * @method onDrag
16782      * @param {Event} e the mousemove event
16783      */
16784     onDrag: function(e) { /* override this */ },
16785
16786     /**
16787      * Abstract method called when this element fist begins hovering over
16788      * another DragDrop obj
16789      * @method onDragEnter
16790      * @param {Event} e the mousemove event
16791      * @param {String|DragDrop[]} id In POINT mode, the element
16792      * id this is hovering over.  In INTERSECT mode, an array of one or more
16793      * dragdrop items being hovered over.
16794      */
16795     onDragEnter: function(e, id) { /* override this */ },
16796
16797     /**
16798      * Code that executes immediately before the onDragOver event
16799      * @method b4DragOver
16800      * @private
16801      */
16802     b4DragOver: function(e) { },
16803
16804     /**
16805      * Abstract method called when this element is hovering over another
16806      * DragDrop obj
16807      * @method onDragOver
16808      * @param {Event} e the mousemove event
16809      * @param {String|DragDrop[]} id In POINT mode, the element
16810      * id this is hovering over.  In INTERSECT mode, an array of dd items
16811      * being hovered over.
16812      */
16813     onDragOver: function(e, id) { /* override this */ },
16814
16815     /**
16816      * Code that executes immediately before the onDragOut event
16817      * @method b4DragOut
16818      * @private
16819      */
16820     b4DragOut: function(e) { },
16821
16822     /**
16823      * Abstract method called when we are no longer hovering over an element
16824      * @method onDragOut
16825      * @param {Event} e the mousemove event
16826      * @param {String|DragDrop[]} id In POINT mode, the element
16827      * id this was hovering over.  In INTERSECT mode, an array of dd items
16828      * that the mouse is no longer over.
16829      */
16830     onDragOut: function(e, id) { /* override this */ },
16831
16832     /**
16833      * Code that executes immediately before the onDragDrop event
16834      * @method b4DragDrop
16835      * @private
16836      */
16837     b4DragDrop: function(e) { },
16838
16839     /**
16840      * Abstract method called when this item is dropped on another DragDrop
16841      * obj
16842      * @method onDragDrop
16843      * @param {Event} e the mouseup event
16844      * @param {String|DragDrop[]} id In POINT mode, the element
16845      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16846      * was dropped on.
16847      */
16848     onDragDrop: function(e, id) { /* override this */ },
16849
16850     /**
16851      * Abstract method called when this item is dropped on an area with no
16852      * drop target
16853      * @method onInvalidDrop
16854      * @param {Event} e the mouseup event
16855      */
16856     onInvalidDrop: function(e) { /* override this */ },
16857
16858     /**
16859      * Code that executes immediately before the endDrag event
16860      * @method b4EndDrag
16861      * @private
16862      */
16863     b4EndDrag: function(e) { },
16864
16865     /**
16866      * Fired when we are done dragging the object
16867      * @method endDrag
16868      * @param {Event} e the mouseup event
16869      */
16870     endDrag: function(e) { /* override this */ },
16871
16872     /**
16873      * Code executed immediately before the onMouseDown event
16874      * @method b4MouseDown
16875      * @param {Event} e the mousedown event
16876      * @private
16877      */
16878     b4MouseDown: function(e) {  },
16879
16880     /**
16881      * Event handler that fires when a drag/drop obj gets a mousedown
16882      * @method onMouseDown
16883      * @param {Event} e the mousedown event
16884      */
16885     onMouseDown: function(e) { /* override this */ },
16886
16887     /**
16888      * Event handler that fires when a drag/drop obj gets a mouseup
16889      * @method onMouseUp
16890      * @param {Event} e the mouseup event
16891      */
16892     onMouseUp: function(e) { /* override this */ },
16893
16894     /**
16895      * Override the onAvailable method to do what is needed after the initial
16896      * position was determined.
16897      * @method onAvailable
16898      */
16899     onAvailable: function () {
16900     },
16901
16902     /*
16903      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16904      * @type Object
16905      */
16906     defaultPadding : {left:0, right:0, top:0, bottom:0},
16907
16908     /*
16909      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16910  *
16911  * Usage:
16912  <pre><code>
16913  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16914                 { dragElId: "existingProxyDiv" });
16915  dd.startDrag = function(){
16916      this.constrainTo("parent-id");
16917  };
16918  </code></pre>
16919  * Or you can initalize it using the {@link Roo.Element} object:
16920  <pre><code>
16921  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16922      startDrag : function(){
16923          this.constrainTo("parent-id");
16924      }
16925  });
16926  </code></pre>
16927      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16928      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16929      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16930      * an object containing the sides to pad. For example: {right:10, bottom:10}
16931      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16932      */
16933     constrainTo : function(constrainTo, pad, inContent){
16934         if(typeof pad == "number"){
16935             pad = {left: pad, right:pad, top:pad, bottom:pad};
16936         }
16937         pad = pad || this.defaultPadding;
16938         var b = Roo.get(this.getEl()).getBox();
16939         var ce = Roo.get(constrainTo);
16940         var s = ce.getScroll();
16941         var c, cd = ce.dom;
16942         if(cd == document.body){
16943             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16944         }else{
16945             xy = ce.getXY();
16946             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16947         }
16948
16949
16950         var topSpace = b.y - c.y;
16951         var leftSpace = b.x - c.x;
16952
16953         this.resetConstraints();
16954         this.setXConstraint(leftSpace - (pad.left||0), // left
16955                 c.width - leftSpace - b.width - (pad.right||0) //right
16956         );
16957         this.setYConstraint(topSpace - (pad.top||0), //top
16958                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16959         );
16960     },
16961
16962     /**
16963      * Returns a reference to the linked element
16964      * @method getEl
16965      * @return {HTMLElement} the html element
16966      */
16967     getEl: function() {
16968         if (!this._domRef) {
16969             this._domRef = Roo.getDom(this.id);
16970         }
16971
16972         return this._domRef;
16973     },
16974
16975     /**
16976      * Returns a reference to the actual element to drag.  By default this is
16977      * the same as the html element, but it can be assigned to another
16978      * element. An example of this can be found in Roo.dd.DDProxy
16979      * @method getDragEl
16980      * @return {HTMLElement} the html element
16981      */
16982     getDragEl: function() {
16983         return Roo.getDom(this.dragElId);
16984     },
16985
16986     /**
16987      * Sets up the DragDrop object.  Must be called in the constructor of any
16988      * Roo.dd.DragDrop subclass
16989      * @method init
16990      * @param id the id of the linked element
16991      * @param {String} sGroup the group of related items
16992      * @param {object} config configuration attributes
16993      */
16994     init: function(id, sGroup, config) {
16995         this.initTarget(id, sGroup, config);
16996         if (!Roo.isTouch) {
16997             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16998         }
16999         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17000         // Event.on(this.id, "selectstart", Event.preventDefault);
17001     },
17002
17003     /**
17004      * Initializes Targeting functionality only... the object does not
17005      * get a mousedown handler.
17006      * @method initTarget
17007      * @param id the id of the linked element
17008      * @param {String} sGroup the group of related items
17009      * @param {object} config configuration attributes
17010      */
17011     initTarget: function(id, sGroup, config) {
17012
17013         // configuration attributes
17014         this.config = config || {};
17015
17016         // create a local reference to the drag and drop manager
17017         this.DDM = Roo.dd.DDM;
17018         // initialize the groups array
17019         this.groups = {};
17020
17021         // assume that we have an element reference instead of an id if the
17022         // parameter is not a string
17023         if (typeof id !== "string") {
17024             id = Roo.id(id);
17025         }
17026
17027         // set the id
17028         this.id = id;
17029
17030         // add to an interaction group
17031         this.addToGroup((sGroup) ? sGroup : "default");
17032
17033         // We don't want to register this as the handle with the manager
17034         // so we just set the id rather than calling the setter.
17035         this.handleElId = id;
17036
17037         // the linked element is the element that gets dragged by default
17038         this.setDragElId(id);
17039
17040         // by default, clicked anchors will not start drag operations.
17041         this.invalidHandleTypes = { A: "A" };
17042         this.invalidHandleIds = {};
17043         this.invalidHandleClasses = [];
17044
17045         this.applyConfig();
17046
17047         this.handleOnAvailable();
17048     },
17049
17050     /**
17051      * Applies the configuration parameters that were passed into the constructor.
17052      * This is supposed to happen at each level through the inheritance chain.  So
17053      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17054      * DragDrop in order to get all of the parameters that are available in
17055      * each object.
17056      * @method applyConfig
17057      */
17058     applyConfig: function() {
17059
17060         // configurable properties:
17061         //    padding, isTarget, maintainOffset, primaryButtonOnly
17062         this.padding           = this.config.padding || [0, 0, 0, 0];
17063         this.isTarget          = (this.config.isTarget !== false);
17064         this.maintainOffset    = (this.config.maintainOffset);
17065         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17066
17067     },
17068
17069     /**
17070      * Executed when the linked element is available
17071      * @method handleOnAvailable
17072      * @private
17073      */
17074     handleOnAvailable: function() {
17075         this.available = true;
17076         this.resetConstraints();
17077         this.onAvailable();
17078     },
17079
17080      /**
17081      * Configures the padding for the target zone in px.  Effectively expands
17082      * (or reduces) the virtual object size for targeting calculations.
17083      * Supports css-style shorthand; if only one parameter is passed, all sides
17084      * will have that padding, and if only two are passed, the top and bottom
17085      * will have the first param, the left and right the second.
17086      * @method setPadding
17087      * @param {int} iTop    Top pad
17088      * @param {int} iRight  Right pad
17089      * @param {int} iBot    Bot pad
17090      * @param {int} iLeft   Left pad
17091      */
17092     setPadding: function(iTop, iRight, iBot, iLeft) {
17093         // this.padding = [iLeft, iRight, iTop, iBot];
17094         if (!iRight && 0 !== iRight) {
17095             this.padding = [iTop, iTop, iTop, iTop];
17096         } else if (!iBot && 0 !== iBot) {
17097             this.padding = [iTop, iRight, iTop, iRight];
17098         } else {
17099             this.padding = [iTop, iRight, iBot, iLeft];
17100         }
17101     },
17102
17103     /**
17104      * Stores the initial placement of the linked element.
17105      * @method setInitialPosition
17106      * @param {int} diffX   the X offset, default 0
17107      * @param {int} diffY   the Y offset, default 0
17108      */
17109     setInitPosition: function(diffX, diffY) {
17110         var el = this.getEl();
17111
17112         if (!this.DDM.verifyEl(el)) {
17113             return;
17114         }
17115
17116         var dx = diffX || 0;
17117         var dy = diffY || 0;
17118
17119         var p = Dom.getXY( el );
17120
17121         this.initPageX = p[0] - dx;
17122         this.initPageY = p[1] - dy;
17123
17124         this.lastPageX = p[0];
17125         this.lastPageY = p[1];
17126
17127
17128         this.setStartPosition(p);
17129     },
17130
17131     /**
17132      * Sets the start position of the element.  This is set when the obj
17133      * is initialized, the reset when a drag is started.
17134      * @method setStartPosition
17135      * @param pos current position (from previous lookup)
17136      * @private
17137      */
17138     setStartPosition: function(pos) {
17139         var p = pos || Dom.getXY( this.getEl() );
17140         this.deltaSetXY = null;
17141
17142         this.startPageX = p[0];
17143         this.startPageY = p[1];
17144     },
17145
17146     /**
17147      * Add this instance to a group of related drag/drop objects.  All
17148      * instances belong to at least one group, and can belong to as many
17149      * groups as needed.
17150      * @method addToGroup
17151      * @param sGroup {string} the name of the group
17152      */
17153     addToGroup: function(sGroup) {
17154         this.groups[sGroup] = true;
17155         this.DDM.regDragDrop(this, sGroup);
17156     },
17157
17158     /**
17159      * Remove's this instance from the supplied interaction group
17160      * @method removeFromGroup
17161      * @param {string}  sGroup  The group to drop
17162      */
17163     removeFromGroup: function(sGroup) {
17164         if (this.groups[sGroup]) {
17165             delete this.groups[sGroup];
17166         }
17167
17168         this.DDM.removeDDFromGroup(this, sGroup);
17169     },
17170
17171     /**
17172      * Allows you to specify that an element other than the linked element
17173      * will be moved with the cursor during a drag
17174      * @method setDragElId
17175      * @param id {string} the id of the element that will be used to initiate the drag
17176      */
17177     setDragElId: function(id) {
17178         this.dragElId = id;
17179     },
17180
17181     /**
17182      * Allows you to specify a child of the linked element that should be
17183      * used to initiate the drag operation.  An example of this would be if
17184      * you have a content div with text and links.  Clicking anywhere in the
17185      * content area would normally start the drag operation.  Use this method
17186      * to specify that an element inside of the content div is the element
17187      * that starts the drag operation.
17188      * @method setHandleElId
17189      * @param id {string} the id of the element that will be used to
17190      * initiate the drag.
17191      */
17192     setHandleElId: function(id) {
17193         if (typeof id !== "string") {
17194             id = Roo.id(id);
17195         }
17196         this.handleElId = id;
17197         this.DDM.regHandle(this.id, id);
17198     },
17199
17200     /**
17201      * Allows you to set an element outside of the linked element as a drag
17202      * handle
17203      * @method setOuterHandleElId
17204      * @param id the id of the element that will be used to initiate the drag
17205      */
17206     setOuterHandleElId: function(id) {
17207         if (typeof id !== "string") {
17208             id = Roo.id(id);
17209         }
17210         Event.on(id, "mousedown",
17211                 this.handleMouseDown, this);
17212         this.setHandleElId(id);
17213
17214         this.hasOuterHandles = true;
17215     },
17216
17217     /**
17218      * Remove all drag and drop hooks for this element
17219      * @method unreg
17220      */
17221     unreg: function() {
17222         Event.un(this.id, "mousedown",
17223                 this.handleMouseDown);
17224         Event.un(this.id, "touchstart",
17225                 this.handleMouseDown);
17226         this._domRef = null;
17227         this.DDM._remove(this);
17228     },
17229
17230     destroy : function(){
17231         this.unreg();
17232     },
17233
17234     /**
17235      * Returns true if this instance is locked, or the drag drop mgr is locked
17236      * (meaning that all drag/drop is disabled on the page.)
17237      * @method isLocked
17238      * @return {boolean} true if this obj or all drag/drop is locked, else
17239      * false
17240      */
17241     isLocked: function() {
17242         return (this.DDM.isLocked() || this.locked);
17243     },
17244
17245     /**
17246      * Fired when this object is clicked
17247      * @method handleMouseDown
17248      * @param {Event} e
17249      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17250      * @private
17251      */
17252     handleMouseDown: function(e, oDD){
17253      
17254         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17255             //Roo.log('not touch/ button !=0');
17256             return;
17257         }
17258         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17259             return; // double touch..
17260         }
17261         
17262
17263         if (this.isLocked()) {
17264             //Roo.log('locked');
17265             return;
17266         }
17267
17268         this.DDM.refreshCache(this.groups);
17269 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17270         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17271         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17272             //Roo.log('no outer handes or not over target');
17273                 // do nothing.
17274         } else {
17275 //            Roo.log('check validator');
17276             if (this.clickValidator(e)) {
17277 //                Roo.log('validate success');
17278                 // set the initial element position
17279                 this.setStartPosition();
17280
17281
17282                 this.b4MouseDown(e);
17283                 this.onMouseDown(e);
17284
17285                 this.DDM.handleMouseDown(e, this);
17286
17287                 this.DDM.stopEvent(e);
17288             } else {
17289
17290
17291             }
17292         }
17293     },
17294
17295     clickValidator: function(e) {
17296         var target = e.getTarget();
17297         return ( this.isValidHandleChild(target) &&
17298                     (this.id == this.handleElId ||
17299                         this.DDM.handleWasClicked(target, this.id)) );
17300     },
17301
17302     /**
17303      * Allows you to specify a tag name that should not start a drag operation
17304      * when clicked.  This is designed to facilitate embedding links within a
17305      * drag handle that do something other than start the drag.
17306      * @method addInvalidHandleType
17307      * @param {string} tagName the type of element to exclude
17308      */
17309     addInvalidHandleType: function(tagName) {
17310         var type = tagName.toUpperCase();
17311         this.invalidHandleTypes[type] = type;
17312     },
17313
17314     /**
17315      * Lets you to specify an element id for a child of a drag handle
17316      * that should not initiate a drag
17317      * @method addInvalidHandleId
17318      * @param {string} id the element id of the element you wish to ignore
17319      */
17320     addInvalidHandleId: function(id) {
17321         if (typeof id !== "string") {
17322             id = Roo.id(id);
17323         }
17324         this.invalidHandleIds[id] = id;
17325     },
17326
17327     /**
17328      * Lets you specify a css class of elements that will not initiate a drag
17329      * @method addInvalidHandleClass
17330      * @param {string} cssClass the class of the elements you wish to ignore
17331      */
17332     addInvalidHandleClass: function(cssClass) {
17333         this.invalidHandleClasses.push(cssClass);
17334     },
17335
17336     /**
17337      * Unsets an excluded tag name set by addInvalidHandleType
17338      * @method removeInvalidHandleType
17339      * @param {string} tagName the type of element to unexclude
17340      */
17341     removeInvalidHandleType: function(tagName) {
17342         var type = tagName.toUpperCase();
17343         // this.invalidHandleTypes[type] = null;
17344         delete this.invalidHandleTypes[type];
17345     },
17346
17347     /**
17348      * Unsets an invalid handle id
17349      * @method removeInvalidHandleId
17350      * @param {string} id the id of the element to re-enable
17351      */
17352     removeInvalidHandleId: function(id) {
17353         if (typeof id !== "string") {
17354             id = Roo.id(id);
17355         }
17356         delete this.invalidHandleIds[id];
17357     },
17358
17359     /**
17360      * Unsets an invalid css class
17361      * @method removeInvalidHandleClass
17362      * @param {string} cssClass the class of the element(s) you wish to
17363      * re-enable
17364      */
17365     removeInvalidHandleClass: function(cssClass) {
17366         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17367             if (this.invalidHandleClasses[i] == cssClass) {
17368                 delete this.invalidHandleClasses[i];
17369             }
17370         }
17371     },
17372
17373     /**
17374      * Checks the tag exclusion list to see if this click should be ignored
17375      * @method isValidHandleChild
17376      * @param {HTMLElement} node the HTMLElement to evaluate
17377      * @return {boolean} true if this is a valid tag type, false if not
17378      */
17379     isValidHandleChild: function(node) {
17380
17381         var valid = true;
17382         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17383         var nodeName;
17384         try {
17385             nodeName = node.nodeName.toUpperCase();
17386         } catch(e) {
17387             nodeName = node.nodeName;
17388         }
17389         valid = valid && !this.invalidHandleTypes[nodeName];
17390         valid = valid && !this.invalidHandleIds[node.id];
17391
17392         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17393             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17394         }
17395
17396
17397         return valid;
17398
17399     },
17400
17401     /**
17402      * Create the array of horizontal tick marks if an interval was specified
17403      * in setXConstraint().
17404      * @method setXTicks
17405      * @private
17406      */
17407     setXTicks: function(iStartX, iTickSize) {
17408         this.xTicks = [];
17409         this.xTickSize = iTickSize;
17410
17411         var tickMap = {};
17412
17413         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17414             if (!tickMap[i]) {
17415                 this.xTicks[this.xTicks.length] = i;
17416                 tickMap[i] = true;
17417             }
17418         }
17419
17420         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17421             if (!tickMap[i]) {
17422                 this.xTicks[this.xTicks.length] = i;
17423                 tickMap[i] = true;
17424             }
17425         }
17426
17427         this.xTicks.sort(this.DDM.numericSort) ;
17428     },
17429
17430     /**
17431      * Create the array of vertical tick marks if an interval was specified in
17432      * setYConstraint().
17433      * @method setYTicks
17434      * @private
17435      */
17436     setYTicks: function(iStartY, iTickSize) {
17437         this.yTicks = [];
17438         this.yTickSize = iTickSize;
17439
17440         var tickMap = {};
17441
17442         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17443             if (!tickMap[i]) {
17444                 this.yTicks[this.yTicks.length] = i;
17445                 tickMap[i] = true;
17446             }
17447         }
17448
17449         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17450             if (!tickMap[i]) {
17451                 this.yTicks[this.yTicks.length] = i;
17452                 tickMap[i] = true;
17453             }
17454         }
17455
17456         this.yTicks.sort(this.DDM.numericSort) ;
17457     },
17458
17459     /**
17460      * By default, the element can be dragged any place on the screen.  Use
17461      * this method to limit the horizontal travel of the element.  Pass in
17462      * 0,0 for the parameters if you want to lock the drag to the y axis.
17463      * @method setXConstraint
17464      * @param {int} iLeft the number of pixels the element can move to the left
17465      * @param {int} iRight the number of pixels the element can move to the
17466      * right
17467      * @param {int} iTickSize optional parameter for specifying that the
17468      * element
17469      * should move iTickSize pixels at a time.
17470      */
17471     setXConstraint: function(iLeft, iRight, iTickSize) {
17472         this.leftConstraint = iLeft;
17473         this.rightConstraint = iRight;
17474
17475         this.minX = this.initPageX - iLeft;
17476         this.maxX = this.initPageX + iRight;
17477         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17478
17479         this.constrainX = true;
17480     },
17481
17482     /**
17483      * Clears any constraints applied to this instance.  Also clears ticks
17484      * since they can't exist independent of a constraint at this time.
17485      * @method clearConstraints
17486      */
17487     clearConstraints: function() {
17488         this.constrainX = false;
17489         this.constrainY = false;
17490         this.clearTicks();
17491     },
17492
17493     /**
17494      * Clears any tick interval defined for this instance
17495      * @method clearTicks
17496      */
17497     clearTicks: function() {
17498         this.xTicks = null;
17499         this.yTicks = null;
17500         this.xTickSize = 0;
17501         this.yTickSize = 0;
17502     },
17503
17504     /**
17505      * By default, the element can be dragged any place on the screen.  Set
17506      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17507      * parameters if you want to lock the drag to the x axis.
17508      * @method setYConstraint
17509      * @param {int} iUp the number of pixels the element can move up
17510      * @param {int} iDown the number of pixels the element can move down
17511      * @param {int} iTickSize optional parameter for specifying that the
17512      * element should move iTickSize pixels at a time.
17513      */
17514     setYConstraint: function(iUp, iDown, iTickSize) {
17515         this.topConstraint = iUp;
17516         this.bottomConstraint = iDown;
17517
17518         this.minY = this.initPageY - iUp;
17519         this.maxY = this.initPageY + iDown;
17520         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17521
17522         this.constrainY = true;
17523
17524     },
17525
17526     /**
17527      * resetConstraints must be called if you manually reposition a dd element.
17528      * @method resetConstraints
17529      * @param {boolean} maintainOffset
17530      */
17531     resetConstraints: function() {
17532
17533
17534         // Maintain offsets if necessary
17535         if (this.initPageX || this.initPageX === 0) {
17536             // figure out how much this thing has moved
17537             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17538             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17539
17540             this.setInitPosition(dx, dy);
17541
17542         // This is the first time we have detected the element's position
17543         } else {
17544             this.setInitPosition();
17545         }
17546
17547         if (this.constrainX) {
17548             this.setXConstraint( this.leftConstraint,
17549                                  this.rightConstraint,
17550                                  this.xTickSize        );
17551         }
17552
17553         if (this.constrainY) {
17554             this.setYConstraint( this.topConstraint,
17555                                  this.bottomConstraint,
17556                                  this.yTickSize         );
17557         }
17558     },
17559
17560     /**
17561      * Normally the drag element is moved pixel by pixel, but we can specify
17562      * that it move a number of pixels at a time.  This method resolves the
17563      * location when we have it set up like this.
17564      * @method getTick
17565      * @param {int} val where we want to place the object
17566      * @param {int[]} tickArray sorted array of valid points
17567      * @return {int} the closest tick
17568      * @private
17569      */
17570     getTick: function(val, tickArray) {
17571
17572         if (!tickArray) {
17573             // If tick interval is not defined, it is effectively 1 pixel,
17574             // so we return the value passed to us.
17575             return val;
17576         } else if (tickArray[0] >= val) {
17577             // The value is lower than the first tick, so we return the first
17578             // tick.
17579             return tickArray[0];
17580         } else {
17581             for (var i=0, len=tickArray.length; i<len; ++i) {
17582                 var next = i + 1;
17583                 if (tickArray[next] && tickArray[next] >= val) {
17584                     var diff1 = val - tickArray[i];
17585                     var diff2 = tickArray[next] - val;
17586                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17587                 }
17588             }
17589
17590             // The value is larger than the last tick, so we return the last
17591             // tick.
17592             return tickArray[tickArray.length - 1];
17593         }
17594     },
17595
17596     /**
17597      * toString method
17598      * @method toString
17599      * @return {string} string representation of the dd obj
17600      */
17601     toString: function() {
17602         return ("DragDrop " + this.id);
17603     }
17604
17605 });
17606
17607 })();
17608 /*
17609  * Based on:
17610  * Ext JS Library 1.1.1
17611  * Copyright(c) 2006-2007, Ext JS, LLC.
17612  *
17613  * Originally Released Under LGPL - original licence link has changed is not relivant.
17614  *
17615  * Fork - LGPL
17616  * <script type="text/javascript">
17617  */
17618
17619
17620 /**
17621  * The drag and drop utility provides a framework for building drag and drop
17622  * applications.  In addition to enabling drag and drop for specific elements,
17623  * the drag and drop elements are tracked by the manager class, and the
17624  * interactions between the various elements are tracked during the drag and
17625  * the implementing code is notified about these important moments.
17626  */
17627
17628 // Only load the library once.  Rewriting the manager class would orphan
17629 // existing drag and drop instances.
17630 if (!Roo.dd.DragDropMgr) {
17631
17632 /**
17633  * @class Roo.dd.DragDropMgr
17634  * DragDropMgr is a singleton that tracks the element interaction for
17635  * all DragDrop items in the window.  Generally, you will not call
17636  * this class directly, but it does have helper methods that could
17637  * be useful in your DragDrop implementations.
17638  * @singleton
17639  */
17640 Roo.dd.DragDropMgr = function() {
17641
17642     var Event = Roo.EventManager;
17643
17644     return {
17645
17646         /**
17647          * Two dimensional Array of registered DragDrop objects.  The first
17648          * dimension is the DragDrop item group, the second the DragDrop
17649          * object.
17650          * @property ids
17651          * @type {string: string}
17652          * @private
17653          * @static
17654          */
17655         ids: {},
17656
17657         /**
17658          * Array of element ids defined as drag handles.  Used to determine
17659          * if the element that generated the mousedown event is actually the
17660          * handle and not the html element itself.
17661          * @property handleIds
17662          * @type {string: string}
17663          * @private
17664          * @static
17665          */
17666         handleIds: {},
17667
17668         /**
17669          * the DragDrop object that is currently being dragged
17670          * @property dragCurrent
17671          * @type DragDrop
17672          * @private
17673          * @static
17674          **/
17675         dragCurrent: null,
17676
17677         /**
17678          * the DragDrop object(s) that are being hovered over
17679          * @property dragOvers
17680          * @type Array
17681          * @private
17682          * @static
17683          */
17684         dragOvers: {},
17685
17686         /**
17687          * the X distance between the cursor and the object being dragged
17688          * @property deltaX
17689          * @type int
17690          * @private
17691          * @static
17692          */
17693         deltaX: 0,
17694
17695         /**
17696          * the Y distance between the cursor and the object being dragged
17697          * @property deltaY
17698          * @type int
17699          * @private
17700          * @static
17701          */
17702         deltaY: 0,
17703
17704         /**
17705          * Flag to determine if we should prevent the default behavior of the
17706          * events we define. By default this is true, but this can be set to
17707          * false if you need the default behavior (not recommended)
17708          * @property preventDefault
17709          * @type boolean
17710          * @static
17711          */
17712         preventDefault: true,
17713
17714         /**
17715          * Flag to determine if we should stop the propagation of the events
17716          * we generate. This is true by default but you may want to set it to
17717          * false if the html element contains other features that require the
17718          * mouse click.
17719          * @property stopPropagation
17720          * @type boolean
17721          * @static
17722          */
17723         stopPropagation: true,
17724
17725         /**
17726          * Internal flag that is set to true when drag and drop has been
17727          * intialized
17728          * @property initialized
17729          * @private
17730          * @static
17731          */
17732         initalized: false,
17733
17734         /**
17735          * All drag and drop can be disabled.
17736          * @property locked
17737          * @private
17738          * @static
17739          */
17740         locked: false,
17741
17742         /**
17743          * Called the first time an element is registered.
17744          * @method init
17745          * @private
17746          * @static
17747          */
17748         init: function() {
17749             this.initialized = true;
17750         },
17751
17752         /**
17753          * In point mode, drag and drop interaction is defined by the
17754          * location of the cursor during the drag/drop
17755          * @property POINT
17756          * @type int
17757          * @static
17758          */
17759         POINT: 0,
17760
17761         /**
17762          * In intersect mode, drag and drop interactio nis defined by the
17763          * overlap of two or more drag and drop objects.
17764          * @property INTERSECT
17765          * @type int
17766          * @static
17767          */
17768         INTERSECT: 1,
17769
17770         /**
17771          * The current drag and drop mode.  Default: POINT
17772          * @property mode
17773          * @type int
17774          * @static
17775          */
17776         mode: 0,
17777
17778         /**
17779          * Runs method on all drag and drop objects
17780          * @method _execOnAll
17781          * @private
17782          * @static
17783          */
17784         _execOnAll: function(sMethod, args) {
17785             for (var i in this.ids) {
17786                 for (var j in this.ids[i]) {
17787                     var oDD = this.ids[i][j];
17788                     if (! this.isTypeOfDD(oDD)) {
17789                         continue;
17790                     }
17791                     oDD[sMethod].apply(oDD, args);
17792                 }
17793             }
17794         },
17795
17796         /**
17797          * Drag and drop initialization.  Sets up the global event handlers
17798          * @method _onLoad
17799          * @private
17800          * @static
17801          */
17802         _onLoad: function() {
17803
17804             this.init();
17805
17806             if (!Roo.isTouch) {
17807                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17808                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17809             }
17810             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17811             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17812             
17813             Event.on(window,   "unload",    this._onUnload, this, true);
17814             Event.on(window,   "resize",    this._onResize, this, true);
17815             // Event.on(window,   "mouseout",    this._test);
17816
17817         },
17818
17819         /**
17820          * Reset constraints on all drag and drop objs
17821          * @method _onResize
17822          * @private
17823          * @static
17824          */
17825         _onResize: function(e) {
17826             this._execOnAll("resetConstraints", []);
17827         },
17828
17829         /**
17830          * Lock all drag and drop functionality
17831          * @method lock
17832          * @static
17833          */
17834         lock: function() { this.locked = true; },
17835
17836         /**
17837          * Unlock all drag and drop functionality
17838          * @method unlock
17839          * @static
17840          */
17841         unlock: function() { this.locked = false; },
17842
17843         /**
17844          * Is drag and drop locked?
17845          * @method isLocked
17846          * @return {boolean} True if drag and drop is locked, false otherwise.
17847          * @static
17848          */
17849         isLocked: function() { return this.locked; },
17850
17851         /**
17852          * Location cache that is set for all drag drop objects when a drag is
17853          * initiated, cleared when the drag is finished.
17854          * @property locationCache
17855          * @private
17856          * @static
17857          */
17858         locationCache: {},
17859
17860         /**
17861          * Set useCache to false if you want to force object the lookup of each
17862          * drag and drop linked element constantly during a drag.
17863          * @property useCache
17864          * @type boolean
17865          * @static
17866          */
17867         useCache: true,
17868
17869         /**
17870          * The number of pixels that the mouse needs to move after the
17871          * mousedown before the drag is initiated.  Default=3;
17872          * @property clickPixelThresh
17873          * @type int
17874          * @static
17875          */
17876         clickPixelThresh: 3,
17877
17878         /**
17879          * The number of milliseconds after the mousedown event to initiate the
17880          * drag if we don't get a mouseup event. Default=1000
17881          * @property clickTimeThresh
17882          * @type int
17883          * @static
17884          */
17885         clickTimeThresh: 350,
17886
17887         /**
17888          * Flag that indicates that either the drag pixel threshold or the
17889          * mousdown time threshold has been met
17890          * @property dragThreshMet
17891          * @type boolean
17892          * @private
17893          * @static
17894          */
17895         dragThreshMet: false,
17896
17897         /**
17898          * Timeout used for the click time threshold
17899          * @property clickTimeout
17900          * @type Object
17901          * @private
17902          * @static
17903          */
17904         clickTimeout: null,
17905
17906         /**
17907          * The X position of the mousedown event stored for later use when a
17908          * drag threshold is met.
17909          * @property startX
17910          * @type int
17911          * @private
17912          * @static
17913          */
17914         startX: 0,
17915
17916         /**
17917          * The Y position of the mousedown event stored for later use when a
17918          * drag threshold is met.
17919          * @property startY
17920          * @type int
17921          * @private
17922          * @static
17923          */
17924         startY: 0,
17925
17926         /**
17927          * Each DragDrop instance must be registered with the DragDropMgr.
17928          * This is executed in DragDrop.init()
17929          * @method regDragDrop
17930          * @param {DragDrop} oDD the DragDrop object to register
17931          * @param {String} sGroup the name of the group this element belongs to
17932          * @static
17933          */
17934         regDragDrop: function(oDD, sGroup) {
17935             if (!this.initialized) { this.init(); }
17936
17937             if (!this.ids[sGroup]) {
17938                 this.ids[sGroup] = {};
17939             }
17940             this.ids[sGroup][oDD.id] = oDD;
17941         },
17942
17943         /**
17944          * Removes the supplied dd instance from the supplied group. Executed
17945          * by DragDrop.removeFromGroup, so don't call this function directly.
17946          * @method removeDDFromGroup
17947          * @private
17948          * @static
17949          */
17950         removeDDFromGroup: function(oDD, sGroup) {
17951             if (!this.ids[sGroup]) {
17952                 this.ids[sGroup] = {};
17953             }
17954
17955             var obj = this.ids[sGroup];
17956             if (obj && obj[oDD.id]) {
17957                 delete obj[oDD.id];
17958             }
17959         },
17960
17961         /**
17962          * Unregisters a drag and drop item.  This is executed in
17963          * DragDrop.unreg, use that method instead of calling this directly.
17964          * @method _remove
17965          * @private
17966          * @static
17967          */
17968         _remove: function(oDD) {
17969             for (var g in oDD.groups) {
17970                 if (g && this.ids[g][oDD.id]) {
17971                     delete this.ids[g][oDD.id];
17972                 }
17973             }
17974             delete this.handleIds[oDD.id];
17975         },
17976
17977         /**
17978          * Each DragDrop handle element must be registered.  This is done
17979          * automatically when executing DragDrop.setHandleElId()
17980          * @method regHandle
17981          * @param {String} sDDId the DragDrop id this element is a handle for
17982          * @param {String} sHandleId the id of the element that is the drag
17983          * handle
17984          * @static
17985          */
17986         regHandle: function(sDDId, sHandleId) {
17987             if (!this.handleIds[sDDId]) {
17988                 this.handleIds[sDDId] = {};
17989             }
17990             this.handleIds[sDDId][sHandleId] = sHandleId;
17991         },
17992
17993         /**
17994          * Utility function to determine if a given element has been
17995          * registered as a drag drop item.
17996          * @method isDragDrop
17997          * @param {String} id the element id to check
17998          * @return {boolean} true if this element is a DragDrop item,
17999          * false otherwise
18000          * @static
18001          */
18002         isDragDrop: function(id) {
18003             return ( this.getDDById(id) ) ? true : false;
18004         },
18005
18006         /**
18007          * Returns the drag and drop instances that are in all groups the
18008          * passed in instance belongs to.
18009          * @method getRelated
18010          * @param {DragDrop} p_oDD the obj to get related data for
18011          * @param {boolean} bTargetsOnly if true, only return targetable objs
18012          * @return {DragDrop[]} the related instances
18013          * @static
18014          */
18015         getRelated: function(p_oDD, bTargetsOnly) {
18016             var oDDs = [];
18017             for (var i in p_oDD.groups) {
18018                 for (j in this.ids[i]) {
18019                     var dd = this.ids[i][j];
18020                     if (! this.isTypeOfDD(dd)) {
18021                         continue;
18022                     }
18023                     if (!bTargetsOnly || dd.isTarget) {
18024                         oDDs[oDDs.length] = dd;
18025                     }
18026                 }
18027             }
18028
18029             return oDDs;
18030         },
18031
18032         /**
18033          * Returns true if the specified dd target is a legal target for
18034          * the specifice drag obj
18035          * @method isLegalTarget
18036          * @param {DragDrop} the drag obj
18037          * @param {DragDrop} the target
18038          * @return {boolean} true if the target is a legal target for the
18039          * dd obj
18040          * @static
18041          */
18042         isLegalTarget: function (oDD, oTargetDD) {
18043             var targets = this.getRelated(oDD, true);
18044             for (var i=0, len=targets.length;i<len;++i) {
18045                 if (targets[i].id == oTargetDD.id) {
18046                     return true;
18047                 }
18048             }
18049
18050             return false;
18051         },
18052
18053         /**
18054          * My goal is to be able to transparently determine if an object is
18055          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18056          * returns "object", oDD.constructor.toString() always returns
18057          * "DragDrop" and not the name of the subclass.  So for now it just
18058          * evaluates a well-known variable in DragDrop.
18059          * @method isTypeOfDD
18060          * @param {Object} the object to evaluate
18061          * @return {boolean} true if typeof oDD = DragDrop
18062          * @static
18063          */
18064         isTypeOfDD: function (oDD) {
18065             return (oDD && oDD.__ygDragDrop);
18066         },
18067
18068         /**
18069          * Utility function to determine if a given element has been
18070          * registered as a drag drop handle for the given Drag Drop object.
18071          * @method isHandle
18072          * @param {String} id the element id to check
18073          * @return {boolean} true if this element is a DragDrop handle, false
18074          * otherwise
18075          * @static
18076          */
18077         isHandle: function(sDDId, sHandleId) {
18078             return ( this.handleIds[sDDId] &&
18079                             this.handleIds[sDDId][sHandleId] );
18080         },
18081
18082         /**
18083          * Returns the DragDrop instance for a given id
18084          * @method getDDById
18085          * @param {String} id the id of the DragDrop object
18086          * @return {DragDrop} the drag drop object, null if it is not found
18087          * @static
18088          */
18089         getDDById: function(id) {
18090             for (var i in this.ids) {
18091                 if (this.ids[i][id]) {
18092                     return this.ids[i][id];
18093                 }
18094             }
18095             return null;
18096         },
18097
18098         /**
18099          * Fired after a registered DragDrop object gets the mousedown event.
18100          * Sets up the events required to track the object being dragged
18101          * @method handleMouseDown
18102          * @param {Event} e the event
18103          * @param oDD the DragDrop object being dragged
18104          * @private
18105          * @static
18106          */
18107         handleMouseDown: function(e, oDD) {
18108             if(Roo.QuickTips){
18109                 Roo.QuickTips.disable();
18110             }
18111             this.currentTarget = e.getTarget();
18112
18113             this.dragCurrent = oDD;
18114
18115             var el = oDD.getEl();
18116
18117             // track start position
18118             this.startX = e.getPageX();
18119             this.startY = e.getPageY();
18120
18121             this.deltaX = this.startX - el.offsetLeft;
18122             this.deltaY = this.startY - el.offsetTop;
18123
18124             this.dragThreshMet = false;
18125
18126             this.clickTimeout = setTimeout(
18127                     function() {
18128                         var DDM = Roo.dd.DDM;
18129                         DDM.startDrag(DDM.startX, DDM.startY);
18130                     },
18131                     this.clickTimeThresh );
18132         },
18133
18134         /**
18135          * Fired when either the drag pixel threshol or the mousedown hold
18136          * time threshold has been met.
18137          * @method startDrag
18138          * @param x {int} the X position of the original mousedown
18139          * @param y {int} the Y position of the original mousedown
18140          * @static
18141          */
18142         startDrag: function(x, y) {
18143             clearTimeout(this.clickTimeout);
18144             if (this.dragCurrent) {
18145                 this.dragCurrent.b4StartDrag(x, y);
18146                 this.dragCurrent.startDrag(x, y);
18147             }
18148             this.dragThreshMet = true;
18149         },
18150
18151         /**
18152          * Internal function to handle the mouseup event.  Will be invoked
18153          * from the context of the document.
18154          * @method handleMouseUp
18155          * @param {Event} e the event
18156          * @private
18157          * @static
18158          */
18159         handleMouseUp: function(e) {
18160
18161             if(Roo.QuickTips){
18162                 Roo.QuickTips.enable();
18163             }
18164             if (! this.dragCurrent) {
18165                 return;
18166             }
18167
18168             clearTimeout(this.clickTimeout);
18169
18170             if (this.dragThreshMet) {
18171                 this.fireEvents(e, true);
18172             } else {
18173             }
18174
18175             this.stopDrag(e);
18176
18177             this.stopEvent(e);
18178         },
18179
18180         /**
18181          * Utility to stop event propagation and event default, if these
18182          * features are turned on.
18183          * @method stopEvent
18184          * @param {Event} e the event as returned by this.getEvent()
18185          * @static
18186          */
18187         stopEvent: function(e){
18188             if(this.stopPropagation) {
18189                 e.stopPropagation();
18190             }
18191
18192             if (this.preventDefault) {
18193                 e.preventDefault();
18194             }
18195         },
18196
18197         /**
18198          * Internal function to clean up event handlers after the drag
18199          * operation is complete
18200          * @method stopDrag
18201          * @param {Event} e the event
18202          * @private
18203          * @static
18204          */
18205         stopDrag: function(e) {
18206             // Fire the drag end event for the item that was dragged
18207             if (this.dragCurrent) {
18208                 if (this.dragThreshMet) {
18209                     this.dragCurrent.b4EndDrag(e);
18210                     this.dragCurrent.endDrag(e);
18211                 }
18212
18213                 this.dragCurrent.onMouseUp(e);
18214             }
18215
18216             this.dragCurrent = null;
18217             this.dragOvers = {};
18218         },
18219
18220         /**
18221          * Internal function to handle the mousemove event.  Will be invoked
18222          * from the context of the html element.
18223          *
18224          * @TODO figure out what we can do about mouse events lost when the
18225          * user drags objects beyond the window boundary.  Currently we can
18226          * detect this in internet explorer by verifying that the mouse is
18227          * down during the mousemove event.  Firefox doesn't give us the
18228          * button state on the mousemove event.
18229          * @method handleMouseMove
18230          * @param {Event} e the event
18231          * @private
18232          * @static
18233          */
18234         handleMouseMove: function(e) {
18235             if (! this.dragCurrent) {
18236                 return true;
18237             }
18238
18239             // var button = e.which || e.button;
18240
18241             // check for IE mouseup outside of page boundary
18242             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18243                 this.stopEvent(e);
18244                 return this.handleMouseUp(e);
18245             }
18246
18247             if (!this.dragThreshMet) {
18248                 var diffX = Math.abs(this.startX - e.getPageX());
18249                 var diffY = Math.abs(this.startY - e.getPageY());
18250                 if (diffX > this.clickPixelThresh ||
18251                             diffY > this.clickPixelThresh) {
18252                     this.startDrag(this.startX, this.startY);
18253                 }
18254             }
18255
18256             if (this.dragThreshMet) {
18257                 this.dragCurrent.b4Drag(e);
18258                 this.dragCurrent.onDrag(e);
18259                 if(!this.dragCurrent.moveOnly){
18260                     this.fireEvents(e, false);
18261                 }
18262             }
18263
18264             this.stopEvent(e);
18265
18266             return true;
18267         },
18268
18269         /**
18270          * Iterates over all of the DragDrop elements to find ones we are
18271          * hovering over or dropping on
18272          * @method fireEvents
18273          * @param {Event} e the event
18274          * @param {boolean} isDrop is this a drop op or a mouseover op?
18275          * @private
18276          * @static
18277          */
18278         fireEvents: function(e, isDrop) {
18279             var dc = this.dragCurrent;
18280
18281             // If the user did the mouse up outside of the window, we could
18282             // get here even though we have ended the drag.
18283             if (!dc || dc.isLocked()) {
18284                 return;
18285             }
18286
18287             var pt = e.getPoint();
18288
18289             // cache the previous dragOver array
18290             var oldOvers = [];
18291
18292             var outEvts   = [];
18293             var overEvts  = [];
18294             var dropEvts  = [];
18295             var enterEvts = [];
18296
18297             // Check to see if the object(s) we were hovering over is no longer
18298             // being hovered over so we can fire the onDragOut event
18299             for (var i in this.dragOvers) {
18300
18301                 var ddo = this.dragOvers[i];
18302
18303                 if (! this.isTypeOfDD(ddo)) {
18304                     continue;
18305                 }
18306
18307                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18308                     outEvts.push( ddo );
18309                 }
18310
18311                 oldOvers[i] = true;
18312                 delete this.dragOvers[i];
18313             }
18314
18315             for (var sGroup in dc.groups) {
18316
18317                 if ("string" != typeof sGroup) {
18318                     continue;
18319                 }
18320
18321                 for (i in this.ids[sGroup]) {
18322                     var oDD = this.ids[sGroup][i];
18323                     if (! this.isTypeOfDD(oDD)) {
18324                         continue;
18325                     }
18326
18327                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18328                         if (this.isOverTarget(pt, oDD, this.mode)) {
18329                             // look for drop interactions
18330                             if (isDrop) {
18331                                 dropEvts.push( oDD );
18332                             // look for drag enter and drag over interactions
18333                             } else {
18334
18335                                 // initial drag over: dragEnter fires
18336                                 if (!oldOvers[oDD.id]) {
18337                                     enterEvts.push( oDD );
18338                                 // subsequent drag overs: dragOver fires
18339                                 } else {
18340                                     overEvts.push( oDD );
18341                                 }
18342
18343                                 this.dragOvers[oDD.id] = oDD;
18344                             }
18345                         }
18346                     }
18347                 }
18348             }
18349
18350             if (this.mode) {
18351                 if (outEvts.length) {
18352                     dc.b4DragOut(e, outEvts);
18353                     dc.onDragOut(e, outEvts);
18354                 }
18355
18356                 if (enterEvts.length) {
18357                     dc.onDragEnter(e, enterEvts);
18358                 }
18359
18360                 if (overEvts.length) {
18361                     dc.b4DragOver(e, overEvts);
18362                     dc.onDragOver(e, overEvts);
18363                 }
18364
18365                 if (dropEvts.length) {
18366                     dc.b4DragDrop(e, dropEvts);
18367                     dc.onDragDrop(e, dropEvts);
18368                 }
18369
18370             } else {
18371                 // fire dragout events
18372                 var len = 0;
18373                 for (i=0, len=outEvts.length; i<len; ++i) {
18374                     dc.b4DragOut(e, outEvts[i].id);
18375                     dc.onDragOut(e, outEvts[i].id);
18376                 }
18377
18378                 // fire enter events
18379                 for (i=0,len=enterEvts.length; i<len; ++i) {
18380                     // dc.b4DragEnter(e, oDD.id);
18381                     dc.onDragEnter(e, enterEvts[i].id);
18382                 }
18383
18384                 // fire over events
18385                 for (i=0,len=overEvts.length; i<len; ++i) {
18386                     dc.b4DragOver(e, overEvts[i].id);
18387                     dc.onDragOver(e, overEvts[i].id);
18388                 }
18389
18390                 // fire drop events
18391                 for (i=0, len=dropEvts.length; i<len; ++i) {
18392                     dc.b4DragDrop(e, dropEvts[i].id);
18393                     dc.onDragDrop(e, dropEvts[i].id);
18394                 }
18395
18396             }
18397
18398             // notify about a drop that did not find a target
18399             if (isDrop && !dropEvts.length) {
18400                 dc.onInvalidDrop(e);
18401             }
18402
18403         },
18404
18405         /**
18406          * Helper function for getting the best match from the list of drag
18407          * and drop objects returned by the drag and drop events when we are
18408          * in INTERSECT mode.  It returns either the first object that the
18409          * cursor is over, or the object that has the greatest overlap with
18410          * the dragged element.
18411          * @method getBestMatch
18412          * @param  {DragDrop[]} dds The array of drag and drop objects
18413          * targeted
18414          * @return {DragDrop}       The best single match
18415          * @static
18416          */
18417         getBestMatch: function(dds) {
18418             var winner = null;
18419             // Return null if the input is not what we expect
18420             //if (!dds || !dds.length || dds.length == 0) {
18421                // winner = null;
18422             // If there is only one item, it wins
18423             //} else if (dds.length == 1) {
18424
18425             var len = dds.length;
18426
18427             if (len == 1) {
18428                 winner = dds[0];
18429             } else {
18430                 // Loop through the targeted items
18431                 for (var i=0; i<len; ++i) {
18432                     var dd = dds[i];
18433                     // If the cursor is over the object, it wins.  If the
18434                     // cursor is over multiple matches, the first one we come
18435                     // to wins.
18436                     if (dd.cursorIsOver) {
18437                         winner = dd;
18438                         break;
18439                     // Otherwise the object with the most overlap wins
18440                     } else {
18441                         if (!winner ||
18442                             winner.overlap.getArea() < dd.overlap.getArea()) {
18443                             winner = dd;
18444                         }
18445                     }
18446                 }
18447             }
18448
18449             return winner;
18450         },
18451
18452         /**
18453          * Refreshes the cache of the top-left and bottom-right points of the
18454          * drag and drop objects in the specified group(s).  This is in the
18455          * format that is stored in the drag and drop instance, so typical
18456          * usage is:
18457          * <code>
18458          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18459          * </code>
18460          * Alternatively:
18461          * <code>
18462          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18463          * </code>
18464          * @TODO this really should be an indexed array.  Alternatively this
18465          * method could accept both.
18466          * @method refreshCache
18467          * @param {Object} groups an associative array of groups to refresh
18468          * @static
18469          */
18470         refreshCache: function(groups) {
18471             for (var sGroup in groups) {
18472                 if ("string" != typeof sGroup) {
18473                     continue;
18474                 }
18475                 for (var i in this.ids[sGroup]) {
18476                     var oDD = this.ids[sGroup][i];
18477
18478                     if (this.isTypeOfDD(oDD)) {
18479                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18480                         var loc = this.getLocation(oDD);
18481                         if (loc) {
18482                             this.locationCache[oDD.id] = loc;
18483                         } else {
18484                             delete this.locationCache[oDD.id];
18485                             // this will unregister the drag and drop object if
18486                             // the element is not in a usable state
18487                             // oDD.unreg();
18488                         }
18489                     }
18490                 }
18491             }
18492         },
18493
18494         /**
18495          * This checks to make sure an element exists and is in the DOM.  The
18496          * main purpose is to handle cases where innerHTML is used to remove
18497          * drag and drop objects from the DOM.  IE provides an 'unspecified
18498          * error' when trying to access the offsetParent of such an element
18499          * @method verifyEl
18500          * @param {HTMLElement} el the element to check
18501          * @return {boolean} true if the element looks usable
18502          * @static
18503          */
18504         verifyEl: function(el) {
18505             if (el) {
18506                 var parent;
18507                 if(Roo.isIE){
18508                     try{
18509                         parent = el.offsetParent;
18510                     }catch(e){}
18511                 }else{
18512                     parent = el.offsetParent;
18513                 }
18514                 if (parent) {
18515                     return true;
18516                 }
18517             }
18518
18519             return false;
18520         },
18521
18522         /**
18523          * Returns a Region object containing the drag and drop element's position
18524          * and size, including the padding configured for it
18525          * @method getLocation
18526          * @param {DragDrop} oDD the drag and drop object to get the
18527          *                       location for
18528          * @return {Roo.lib.Region} a Region object representing the total area
18529          *                             the element occupies, including any padding
18530          *                             the instance is configured for.
18531          * @static
18532          */
18533         getLocation: function(oDD) {
18534             if (! this.isTypeOfDD(oDD)) {
18535                 return null;
18536             }
18537
18538             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18539
18540             try {
18541                 pos= Roo.lib.Dom.getXY(el);
18542             } catch (e) { }
18543
18544             if (!pos) {
18545                 return null;
18546             }
18547
18548             x1 = pos[0];
18549             x2 = x1 + el.offsetWidth;
18550             y1 = pos[1];
18551             y2 = y1 + el.offsetHeight;
18552
18553             t = y1 - oDD.padding[0];
18554             r = x2 + oDD.padding[1];
18555             b = y2 + oDD.padding[2];
18556             l = x1 - oDD.padding[3];
18557
18558             return new Roo.lib.Region( t, r, b, l );
18559         },
18560
18561         /**
18562          * Checks the cursor location to see if it over the target
18563          * @method isOverTarget
18564          * @param {Roo.lib.Point} pt The point to evaluate
18565          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18566          * @return {boolean} true if the mouse is over the target
18567          * @private
18568          * @static
18569          */
18570         isOverTarget: function(pt, oTarget, intersect) {
18571             // use cache if available
18572             var loc = this.locationCache[oTarget.id];
18573             if (!loc || !this.useCache) {
18574                 loc = this.getLocation(oTarget);
18575                 this.locationCache[oTarget.id] = loc;
18576
18577             }
18578
18579             if (!loc) {
18580                 return false;
18581             }
18582
18583             oTarget.cursorIsOver = loc.contains( pt );
18584
18585             // DragDrop is using this as a sanity check for the initial mousedown
18586             // in this case we are done.  In POINT mode, if the drag obj has no
18587             // contraints, we are also done. Otherwise we need to evaluate the
18588             // location of the target as related to the actual location of the
18589             // dragged element.
18590             var dc = this.dragCurrent;
18591             if (!dc || !dc.getTargetCoord ||
18592                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18593                 return oTarget.cursorIsOver;
18594             }
18595
18596             oTarget.overlap = null;
18597
18598             // Get the current location of the drag element, this is the
18599             // location of the mouse event less the delta that represents
18600             // where the original mousedown happened on the element.  We
18601             // need to consider constraints and ticks as well.
18602             var pos = dc.getTargetCoord(pt.x, pt.y);
18603
18604             var el = dc.getDragEl();
18605             var curRegion = new Roo.lib.Region( pos.y,
18606                                                    pos.x + el.offsetWidth,
18607                                                    pos.y + el.offsetHeight,
18608                                                    pos.x );
18609
18610             var overlap = curRegion.intersect(loc);
18611
18612             if (overlap) {
18613                 oTarget.overlap = overlap;
18614                 return (intersect) ? true : oTarget.cursorIsOver;
18615             } else {
18616                 return false;
18617             }
18618         },
18619
18620         /**
18621          * unload event handler
18622          * @method _onUnload
18623          * @private
18624          * @static
18625          */
18626         _onUnload: function(e, me) {
18627             Roo.dd.DragDropMgr.unregAll();
18628         },
18629
18630         /**
18631          * Cleans up the drag and drop events and objects.
18632          * @method unregAll
18633          * @private
18634          * @static
18635          */
18636         unregAll: function() {
18637
18638             if (this.dragCurrent) {
18639                 this.stopDrag();
18640                 this.dragCurrent = null;
18641             }
18642
18643             this._execOnAll("unreg", []);
18644
18645             for (i in this.elementCache) {
18646                 delete this.elementCache[i];
18647             }
18648
18649             this.elementCache = {};
18650             this.ids = {};
18651         },
18652
18653         /**
18654          * A cache of DOM elements
18655          * @property elementCache
18656          * @private
18657          * @static
18658          */
18659         elementCache: {},
18660
18661         /**
18662          * Get the wrapper for the DOM element specified
18663          * @method getElWrapper
18664          * @param {String} id the id of the element to get
18665          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18666          * @private
18667          * @deprecated This wrapper isn't that useful
18668          * @static
18669          */
18670         getElWrapper: function(id) {
18671             var oWrapper = this.elementCache[id];
18672             if (!oWrapper || !oWrapper.el) {
18673                 oWrapper = this.elementCache[id] =
18674                     new this.ElementWrapper(Roo.getDom(id));
18675             }
18676             return oWrapper;
18677         },
18678
18679         /**
18680          * Returns the actual DOM element
18681          * @method getElement
18682          * @param {String} id the id of the elment to get
18683          * @return {Object} The element
18684          * @deprecated use Roo.getDom instead
18685          * @static
18686          */
18687         getElement: function(id) {
18688             return Roo.getDom(id);
18689         },
18690
18691         /**
18692          * Returns the style property for the DOM element (i.e.,
18693          * document.getElById(id).style)
18694          * @method getCss
18695          * @param {String} id the id of the elment to get
18696          * @return {Object} The style property of the element
18697          * @deprecated use Roo.getDom instead
18698          * @static
18699          */
18700         getCss: function(id) {
18701             var el = Roo.getDom(id);
18702             return (el) ? el.style : null;
18703         },
18704
18705         /**
18706          * Inner class for cached elements
18707          * @class DragDropMgr.ElementWrapper
18708          * @for DragDropMgr
18709          * @private
18710          * @deprecated
18711          */
18712         ElementWrapper: function(el) {
18713                 /**
18714                  * The element
18715                  * @property el
18716                  */
18717                 this.el = el || null;
18718                 /**
18719                  * The element id
18720                  * @property id
18721                  */
18722                 this.id = this.el && el.id;
18723                 /**
18724                  * A reference to the style property
18725                  * @property css
18726                  */
18727                 this.css = this.el && el.style;
18728             },
18729
18730         /**
18731          * Returns the X position of an html element
18732          * @method getPosX
18733          * @param el the element for which to get the position
18734          * @return {int} the X coordinate
18735          * @for DragDropMgr
18736          * @deprecated use Roo.lib.Dom.getX instead
18737          * @static
18738          */
18739         getPosX: function(el) {
18740             return Roo.lib.Dom.getX(el);
18741         },
18742
18743         /**
18744          * Returns the Y position of an html element
18745          * @method getPosY
18746          * @param el the element for which to get the position
18747          * @return {int} the Y coordinate
18748          * @deprecated use Roo.lib.Dom.getY instead
18749          * @static
18750          */
18751         getPosY: function(el) {
18752             return Roo.lib.Dom.getY(el);
18753         },
18754
18755         /**
18756          * Swap two nodes.  In IE, we use the native method, for others we
18757          * emulate the IE behavior
18758          * @method swapNode
18759          * @param n1 the first node to swap
18760          * @param n2 the other node to swap
18761          * @static
18762          */
18763         swapNode: function(n1, n2) {
18764             if (n1.swapNode) {
18765                 n1.swapNode(n2);
18766             } else {
18767                 var p = n2.parentNode;
18768                 var s = n2.nextSibling;
18769
18770                 if (s == n1) {
18771                     p.insertBefore(n1, n2);
18772                 } else if (n2 == n1.nextSibling) {
18773                     p.insertBefore(n2, n1);
18774                 } else {
18775                     n1.parentNode.replaceChild(n2, n1);
18776                     p.insertBefore(n1, s);
18777                 }
18778             }
18779         },
18780
18781         /**
18782          * Returns the current scroll position
18783          * @method getScroll
18784          * @private
18785          * @static
18786          */
18787         getScroll: function () {
18788             var t, l, dde=document.documentElement, db=document.body;
18789             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18790                 t = dde.scrollTop;
18791                 l = dde.scrollLeft;
18792             } else if (db) {
18793                 t = db.scrollTop;
18794                 l = db.scrollLeft;
18795             } else {
18796
18797             }
18798             return { top: t, left: l };
18799         },
18800
18801         /**
18802          * Returns the specified element style property
18803          * @method getStyle
18804          * @param {HTMLElement} el          the element
18805          * @param {string}      styleProp   the style property
18806          * @return {string} The value of the style property
18807          * @deprecated use Roo.lib.Dom.getStyle
18808          * @static
18809          */
18810         getStyle: function(el, styleProp) {
18811             return Roo.fly(el).getStyle(styleProp);
18812         },
18813
18814         /**
18815          * Gets the scrollTop
18816          * @method getScrollTop
18817          * @return {int} the document's scrollTop
18818          * @static
18819          */
18820         getScrollTop: function () { return this.getScroll().top; },
18821
18822         /**
18823          * Gets the scrollLeft
18824          * @method getScrollLeft
18825          * @return {int} the document's scrollTop
18826          * @static
18827          */
18828         getScrollLeft: function () { return this.getScroll().left; },
18829
18830         /**
18831          * Sets the x/y position of an element to the location of the
18832          * target element.
18833          * @method moveToEl
18834          * @param {HTMLElement} moveEl      The element to move
18835          * @param {HTMLElement} targetEl    The position reference element
18836          * @static
18837          */
18838         moveToEl: function (moveEl, targetEl) {
18839             var aCoord = Roo.lib.Dom.getXY(targetEl);
18840             Roo.lib.Dom.setXY(moveEl, aCoord);
18841         },
18842
18843         /**
18844          * Numeric array sort function
18845          * @method numericSort
18846          * @static
18847          */
18848         numericSort: function(a, b) { return (a - b); },
18849
18850         /**
18851          * Internal counter
18852          * @property _timeoutCount
18853          * @private
18854          * @static
18855          */
18856         _timeoutCount: 0,
18857
18858         /**
18859          * Trying to make the load order less important.  Without this we get
18860          * an error if this file is loaded before the Event Utility.
18861          * @method _addListeners
18862          * @private
18863          * @static
18864          */
18865         _addListeners: function() {
18866             var DDM = Roo.dd.DDM;
18867             if ( Roo.lib.Event && document ) {
18868                 DDM._onLoad();
18869             } else {
18870                 if (DDM._timeoutCount > 2000) {
18871                 } else {
18872                     setTimeout(DDM._addListeners, 10);
18873                     if (document && document.body) {
18874                         DDM._timeoutCount += 1;
18875                     }
18876                 }
18877             }
18878         },
18879
18880         /**
18881          * Recursively searches the immediate parent and all child nodes for
18882          * the handle element in order to determine wheter or not it was
18883          * clicked.
18884          * @method handleWasClicked
18885          * @param node the html element to inspect
18886          * @static
18887          */
18888         handleWasClicked: function(node, id) {
18889             if (this.isHandle(id, node.id)) {
18890                 return true;
18891             } else {
18892                 // check to see if this is a text node child of the one we want
18893                 var p = node.parentNode;
18894
18895                 while (p) {
18896                     if (this.isHandle(id, p.id)) {
18897                         return true;
18898                     } else {
18899                         p = p.parentNode;
18900                     }
18901                 }
18902             }
18903
18904             return false;
18905         }
18906
18907     };
18908
18909 }();
18910
18911 // shorter alias, save a few bytes
18912 Roo.dd.DDM = Roo.dd.DragDropMgr;
18913 Roo.dd.DDM._addListeners();
18914
18915 }/*
18916  * Based on:
18917  * Ext JS Library 1.1.1
18918  * Copyright(c) 2006-2007, Ext JS, LLC.
18919  *
18920  * Originally Released Under LGPL - original licence link has changed is not relivant.
18921  *
18922  * Fork - LGPL
18923  * <script type="text/javascript">
18924  */
18925
18926 /**
18927  * @class Roo.dd.DD
18928  * A DragDrop implementation where the linked element follows the
18929  * mouse cursor during a drag.
18930  * @extends Roo.dd.DragDrop
18931  * @constructor
18932  * @param {String} id the id of the linked element
18933  * @param {String} sGroup the group of related DragDrop items
18934  * @param {object} config an object containing configurable attributes
18935  *                Valid properties for DD:
18936  *                    scroll
18937  */
18938 Roo.dd.DD = function(id, sGroup, config) {
18939     if (id) {
18940         this.init(id, sGroup, config);
18941     }
18942 };
18943
18944 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18945
18946     /**
18947      * When set to true, the utility automatically tries to scroll the browser
18948      * window wehn a drag and drop element is dragged near the viewport boundary.
18949      * Defaults to true.
18950      * @property scroll
18951      * @type boolean
18952      */
18953     scroll: true,
18954
18955     /**
18956      * Sets the pointer offset to the distance between the linked element's top
18957      * left corner and the location the element was clicked
18958      * @method autoOffset
18959      * @param {int} iPageX the X coordinate of the click
18960      * @param {int} iPageY the Y coordinate of the click
18961      */
18962     autoOffset: function(iPageX, iPageY) {
18963         var x = iPageX - this.startPageX;
18964         var y = iPageY - this.startPageY;
18965         this.setDelta(x, y);
18966     },
18967
18968     /**
18969      * Sets the pointer offset.  You can call this directly to force the
18970      * offset to be in a particular location (e.g., pass in 0,0 to set it
18971      * to the center of the object)
18972      * @method setDelta
18973      * @param {int} iDeltaX the distance from the left
18974      * @param {int} iDeltaY the distance from the top
18975      */
18976     setDelta: function(iDeltaX, iDeltaY) {
18977         this.deltaX = iDeltaX;
18978         this.deltaY = iDeltaY;
18979     },
18980
18981     /**
18982      * Sets the drag element to the location of the mousedown or click event,
18983      * maintaining the cursor location relative to the location on the element
18984      * that was clicked.  Override this if you want to place the element in a
18985      * location other than where the cursor is.
18986      * @method setDragElPos
18987      * @param {int} iPageX the X coordinate of the mousedown or drag event
18988      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18989      */
18990     setDragElPos: function(iPageX, iPageY) {
18991         // the first time we do this, we are going to check to make sure
18992         // the element has css positioning
18993
18994         var el = this.getDragEl();
18995         this.alignElWithMouse(el, iPageX, iPageY);
18996     },
18997
18998     /**
18999      * Sets the element to the location of the mousedown or click event,
19000      * maintaining the cursor location relative to the location on the element
19001      * that was clicked.  Override this if you want to place the element in a
19002      * location other than where the cursor is.
19003      * @method alignElWithMouse
19004      * @param {HTMLElement} el the element to move
19005      * @param {int} iPageX the X coordinate of the mousedown or drag event
19006      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19007      */
19008     alignElWithMouse: function(el, iPageX, iPageY) {
19009         var oCoord = this.getTargetCoord(iPageX, iPageY);
19010         var fly = el.dom ? el : Roo.fly(el);
19011         if (!this.deltaSetXY) {
19012             var aCoord = [oCoord.x, oCoord.y];
19013             fly.setXY(aCoord);
19014             var newLeft = fly.getLeft(true);
19015             var newTop  = fly.getTop(true);
19016             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19017         } else {
19018             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19019         }
19020
19021         this.cachePosition(oCoord.x, oCoord.y);
19022         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19023         return oCoord;
19024     },
19025
19026     /**
19027      * Saves the most recent position so that we can reset the constraints and
19028      * tick marks on-demand.  We need to know this so that we can calculate the
19029      * number of pixels the element is offset from its original position.
19030      * @method cachePosition
19031      * @param iPageX the current x position (optional, this just makes it so we
19032      * don't have to look it up again)
19033      * @param iPageY the current y position (optional, this just makes it so we
19034      * don't have to look it up again)
19035      */
19036     cachePosition: function(iPageX, iPageY) {
19037         if (iPageX) {
19038             this.lastPageX = iPageX;
19039             this.lastPageY = iPageY;
19040         } else {
19041             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19042             this.lastPageX = aCoord[0];
19043             this.lastPageY = aCoord[1];
19044         }
19045     },
19046
19047     /**
19048      * Auto-scroll the window if the dragged object has been moved beyond the
19049      * visible window boundary.
19050      * @method autoScroll
19051      * @param {int} x the drag element's x position
19052      * @param {int} y the drag element's y position
19053      * @param {int} h the height of the drag element
19054      * @param {int} w the width of the drag element
19055      * @private
19056      */
19057     autoScroll: function(x, y, h, w) {
19058
19059         if (this.scroll) {
19060             // The client height
19061             var clientH = Roo.lib.Dom.getViewWidth();
19062
19063             // The client width
19064             var clientW = Roo.lib.Dom.getViewHeight();
19065
19066             // The amt scrolled down
19067             var st = this.DDM.getScrollTop();
19068
19069             // The amt scrolled right
19070             var sl = this.DDM.getScrollLeft();
19071
19072             // Location of the bottom of the element
19073             var bot = h + y;
19074
19075             // Location of the right of the element
19076             var right = w + x;
19077
19078             // The distance from the cursor to the bottom of the visible area,
19079             // adjusted so that we don't scroll if the cursor is beyond the
19080             // element drag constraints
19081             var toBot = (clientH + st - y - this.deltaY);
19082
19083             // The distance from the cursor to the right of the visible area
19084             var toRight = (clientW + sl - x - this.deltaX);
19085
19086
19087             // How close to the edge the cursor must be before we scroll
19088             // var thresh = (document.all) ? 100 : 40;
19089             var thresh = 40;
19090
19091             // How many pixels to scroll per autoscroll op.  This helps to reduce
19092             // clunky scrolling. IE is more sensitive about this ... it needs this
19093             // value to be higher.
19094             var scrAmt = (document.all) ? 80 : 30;
19095
19096             // Scroll down if we are near the bottom of the visible page and the
19097             // obj extends below the crease
19098             if ( bot > clientH && toBot < thresh ) {
19099                 window.scrollTo(sl, st + scrAmt);
19100             }
19101
19102             // Scroll up if the window is scrolled down and the top of the object
19103             // goes above the top border
19104             if ( y < st && st > 0 && y - st < thresh ) {
19105                 window.scrollTo(sl, st - scrAmt);
19106             }
19107
19108             // Scroll right if the obj is beyond the right border and the cursor is
19109             // near the border.
19110             if ( right > clientW && toRight < thresh ) {
19111                 window.scrollTo(sl + scrAmt, st);
19112             }
19113
19114             // Scroll left if the window has been scrolled to the right and the obj
19115             // extends past the left border
19116             if ( x < sl && sl > 0 && x - sl < thresh ) {
19117                 window.scrollTo(sl - scrAmt, st);
19118             }
19119         }
19120     },
19121
19122     /**
19123      * Finds the location the element should be placed if we want to move
19124      * it to where the mouse location less the click offset would place us.
19125      * @method getTargetCoord
19126      * @param {int} iPageX the X coordinate of the click
19127      * @param {int} iPageY the Y coordinate of the click
19128      * @return an object that contains the coordinates (Object.x and Object.y)
19129      * @private
19130      */
19131     getTargetCoord: function(iPageX, iPageY) {
19132
19133
19134         var x = iPageX - this.deltaX;
19135         var y = iPageY - this.deltaY;
19136
19137         if (this.constrainX) {
19138             if (x < this.minX) { x = this.minX; }
19139             if (x > this.maxX) { x = this.maxX; }
19140         }
19141
19142         if (this.constrainY) {
19143             if (y < this.minY) { y = this.minY; }
19144             if (y > this.maxY) { y = this.maxY; }
19145         }
19146
19147         x = this.getTick(x, this.xTicks);
19148         y = this.getTick(y, this.yTicks);
19149
19150
19151         return {x:x, y:y};
19152     },
19153
19154     /*
19155      * Sets up config options specific to this class. Overrides
19156      * Roo.dd.DragDrop, but all versions of this method through the
19157      * inheritance chain are called
19158      */
19159     applyConfig: function() {
19160         Roo.dd.DD.superclass.applyConfig.call(this);
19161         this.scroll = (this.config.scroll !== false);
19162     },
19163
19164     /*
19165      * Event that fires prior to the onMouseDown event.  Overrides
19166      * Roo.dd.DragDrop.
19167      */
19168     b4MouseDown: function(e) {
19169         // this.resetConstraints();
19170         this.autoOffset(e.getPageX(),
19171                             e.getPageY());
19172     },
19173
19174     /*
19175      * Event that fires prior to the onDrag event.  Overrides
19176      * Roo.dd.DragDrop.
19177      */
19178     b4Drag: function(e) {
19179         this.setDragElPos(e.getPageX(),
19180                             e.getPageY());
19181     },
19182
19183     toString: function() {
19184         return ("DD " + this.id);
19185     }
19186
19187     //////////////////////////////////////////////////////////////////////////
19188     // Debugging ygDragDrop events that can be overridden
19189     //////////////////////////////////////////////////////////////////////////
19190     /*
19191     startDrag: function(x, y) {
19192     },
19193
19194     onDrag: function(e) {
19195     },
19196
19197     onDragEnter: function(e, id) {
19198     },
19199
19200     onDragOver: function(e, id) {
19201     },
19202
19203     onDragOut: function(e, id) {
19204     },
19205
19206     onDragDrop: function(e, id) {
19207     },
19208
19209     endDrag: function(e) {
19210     }
19211
19212     */
19213
19214 });/*
19215  * Based on:
19216  * Ext JS Library 1.1.1
19217  * Copyright(c) 2006-2007, Ext JS, LLC.
19218  *
19219  * Originally Released Under LGPL - original licence link has changed is not relivant.
19220  *
19221  * Fork - LGPL
19222  * <script type="text/javascript">
19223  */
19224
19225 /**
19226  * @class Roo.dd.DDProxy
19227  * A DragDrop implementation that inserts an empty, bordered div into
19228  * the document that follows the cursor during drag operations.  At the time of
19229  * the click, the frame div is resized to the dimensions of the linked html
19230  * element, and moved to the exact location of the linked element.
19231  *
19232  * References to the "frame" element refer to the single proxy element that
19233  * was created to be dragged in place of all DDProxy elements on the
19234  * page.
19235  *
19236  * @extends Roo.dd.DD
19237  * @constructor
19238  * @param {String} id the id of the linked html element
19239  * @param {String} sGroup the group of related DragDrop objects
19240  * @param {object} config an object containing configurable attributes
19241  *                Valid properties for DDProxy in addition to those in DragDrop:
19242  *                   resizeFrame, centerFrame, dragElId
19243  */
19244 Roo.dd.DDProxy = function(id, sGroup, config) {
19245     if (id) {
19246         this.init(id, sGroup, config);
19247         this.initFrame();
19248     }
19249 };
19250
19251 /**
19252  * The default drag frame div id
19253  * @property Roo.dd.DDProxy.dragElId
19254  * @type String
19255  * @static
19256  */
19257 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19258
19259 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19260
19261     /**
19262      * By default we resize the drag frame to be the same size as the element
19263      * we want to drag (this is to get the frame effect).  We can turn it off
19264      * if we want a different behavior.
19265      * @property resizeFrame
19266      * @type boolean
19267      */
19268     resizeFrame: true,
19269
19270     /**
19271      * By default the frame is positioned exactly where the drag element is, so
19272      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19273      * you do not have constraints on the obj is to have the drag frame centered
19274      * around the cursor.  Set centerFrame to true for this effect.
19275      * @property centerFrame
19276      * @type boolean
19277      */
19278     centerFrame: false,
19279
19280     /**
19281      * Creates the proxy element if it does not yet exist
19282      * @method createFrame
19283      */
19284     createFrame: function() {
19285         var self = this;
19286         var body = document.body;
19287
19288         if (!body || !body.firstChild) {
19289             setTimeout( function() { self.createFrame(); }, 50 );
19290             return;
19291         }
19292
19293         var div = this.getDragEl();
19294
19295         if (!div) {
19296             div    = document.createElement("div");
19297             div.id = this.dragElId;
19298             var s  = div.style;
19299
19300             s.position   = "absolute";
19301             s.visibility = "hidden";
19302             s.cursor     = "move";
19303             s.border     = "2px solid #aaa";
19304             s.zIndex     = 999;
19305
19306             // appendChild can blow up IE if invoked prior to the window load event
19307             // while rendering a table.  It is possible there are other scenarios
19308             // that would cause this to happen as well.
19309             body.insertBefore(div, body.firstChild);
19310         }
19311     },
19312
19313     /**
19314      * Initialization for the drag frame element.  Must be called in the
19315      * constructor of all subclasses
19316      * @method initFrame
19317      */
19318     initFrame: function() {
19319         this.createFrame();
19320     },
19321
19322     applyConfig: function() {
19323         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19324
19325         this.resizeFrame = (this.config.resizeFrame !== false);
19326         this.centerFrame = (this.config.centerFrame);
19327         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19328     },
19329
19330     /**
19331      * Resizes the drag frame to the dimensions of the clicked object, positions
19332      * it over the object, and finally displays it
19333      * @method showFrame
19334      * @param {int} iPageX X click position
19335      * @param {int} iPageY Y click position
19336      * @private
19337      */
19338     showFrame: function(iPageX, iPageY) {
19339         var el = this.getEl();
19340         var dragEl = this.getDragEl();
19341         var s = dragEl.style;
19342
19343         this._resizeProxy();
19344
19345         if (this.centerFrame) {
19346             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19347                            Math.round(parseInt(s.height, 10)/2) );
19348         }
19349
19350         this.setDragElPos(iPageX, iPageY);
19351
19352         Roo.fly(dragEl).show();
19353     },
19354
19355     /**
19356      * The proxy is automatically resized to the dimensions of the linked
19357      * element when a drag is initiated, unless resizeFrame is set to false
19358      * @method _resizeProxy
19359      * @private
19360      */
19361     _resizeProxy: function() {
19362         if (this.resizeFrame) {
19363             var el = this.getEl();
19364             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19365         }
19366     },
19367
19368     // overrides Roo.dd.DragDrop
19369     b4MouseDown: function(e) {
19370         var x = e.getPageX();
19371         var y = e.getPageY();
19372         this.autoOffset(x, y);
19373         this.setDragElPos(x, y);
19374     },
19375
19376     // overrides Roo.dd.DragDrop
19377     b4StartDrag: function(x, y) {
19378         // show the drag frame
19379         this.showFrame(x, y);
19380     },
19381
19382     // overrides Roo.dd.DragDrop
19383     b4EndDrag: function(e) {
19384         Roo.fly(this.getDragEl()).hide();
19385     },
19386
19387     // overrides Roo.dd.DragDrop
19388     // By default we try to move the element to the last location of the frame.
19389     // This is so that the default behavior mirrors that of Roo.dd.DD.
19390     endDrag: function(e) {
19391
19392         var lel = this.getEl();
19393         var del = this.getDragEl();
19394
19395         // Show the drag frame briefly so we can get its position
19396         del.style.visibility = "";
19397
19398         this.beforeMove();
19399         // Hide the linked element before the move to get around a Safari
19400         // rendering bug.
19401         lel.style.visibility = "hidden";
19402         Roo.dd.DDM.moveToEl(lel, del);
19403         del.style.visibility = "hidden";
19404         lel.style.visibility = "";
19405
19406         this.afterDrag();
19407     },
19408
19409     beforeMove : function(){
19410
19411     },
19412
19413     afterDrag : function(){
19414
19415     },
19416
19417     toString: function() {
19418         return ("DDProxy " + this.id);
19419     }
19420
19421 });
19422 /*
19423  * Based on:
19424  * Ext JS Library 1.1.1
19425  * Copyright(c) 2006-2007, Ext JS, LLC.
19426  *
19427  * Originally Released Under LGPL - original licence link has changed is not relivant.
19428  *
19429  * Fork - LGPL
19430  * <script type="text/javascript">
19431  */
19432
19433  /**
19434  * @class Roo.dd.DDTarget
19435  * A DragDrop implementation that does not move, but can be a drop
19436  * target.  You would get the same result by simply omitting implementation
19437  * for the event callbacks, but this way we reduce the processing cost of the
19438  * event listener and the callbacks.
19439  * @extends Roo.dd.DragDrop
19440  * @constructor
19441  * @param {String} id the id of the element that is a drop target
19442  * @param {String} sGroup the group of related DragDrop objects
19443  * @param {object} config an object containing configurable attributes
19444  *                 Valid properties for DDTarget in addition to those in
19445  *                 DragDrop:
19446  *                    none
19447  */
19448 Roo.dd.DDTarget = function(id, sGroup, config) {
19449     if (id) {
19450         this.initTarget(id, sGroup, config);
19451     }
19452     if (config.listeners || config.events) { 
19453        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19454             listeners : config.listeners || {}, 
19455             events : config.events || {} 
19456         });    
19457     }
19458 };
19459
19460 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19461 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19462     toString: function() {
19463         return ("DDTarget " + this.id);
19464     }
19465 });
19466 /*
19467  * Based on:
19468  * Ext JS Library 1.1.1
19469  * Copyright(c) 2006-2007, Ext JS, LLC.
19470  *
19471  * Originally Released Under LGPL - original licence link has changed is not relivant.
19472  *
19473  * Fork - LGPL
19474  * <script type="text/javascript">
19475  */
19476  
19477
19478 /**
19479  * @class Roo.dd.ScrollManager
19480  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19481  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19482  * @singleton
19483  */
19484 Roo.dd.ScrollManager = function(){
19485     var ddm = Roo.dd.DragDropMgr;
19486     var els = {};
19487     var dragEl = null;
19488     var proc = {};
19489     
19490     
19491     
19492     var onStop = function(e){
19493         dragEl = null;
19494         clearProc();
19495     };
19496     
19497     var triggerRefresh = function(){
19498         if(ddm.dragCurrent){
19499              ddm.refreshCache(ddm.dragCurrent.groups);
19500         }
19501     };
19502     
19503     var doScroll = function(){
19504         if(ddm.dragCurrent){
19505             var dds = Roo.dd.ScrollManager;
19506             if(!dds.animate){
19507                 if(proc.el.scroll(proc.dir, dds.increment)){
19508                     triggerRefresh();
19509                 }
19510             }else{
19511                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19512             }
19513         }
19514     };
19515     
19516     var clearProc = function(){
19517         if(proc.id){
19518             clearInterval(proc.id);
19519         }
19520         proc.id = 0;
19521         proc.el = null;
19522         proc.dir = "";
19523     };
19524     
19525     var startProc = function(el, dir){
19526          Roo.log('scroll startproc');
19527         clearProc();
19528         proc.el = el;
19529         proc.dir = dir;
19530         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19531     };
19532     
19533     var onFire = function(e, isDrop){
19534        
19535         if(isDrop || !ddm.dragCurrent){ return; }
19536         var dds = Roo.dd.ScrollManager;
19537         if(!dragEl || dragEl != ddm.dragCurrent){
19538             dragEl = ddm.dragCurrent;
19539             // refresh regions on drag start
19540             dds.refreshCache();
19541         }
19542         
19543         var xy = Roo.lib.Event.getXY(e);
19544         var pt = new Roo.lib.Point(xy[0], xy[1]);
19545         for(var id in els){
19546             var el = els[id], r = el._region;
19547             if(r && r.contains(pt) && el.isScrollable()){
19548                 if(r.bottom - pt.y <= dds.thresh){
19549                     if(proc.el != el){
19550                         startProc(el, "down");
19551                     }
19552                     return;
19553                 }else if(r.right - pt.x <= dds.thresh){
19554                     if(proc.el != el){
19555                         startProc(el, "left");
19556                     }
19557                     return;
19558                 }else if(pt.y - r.top <= dds.thresh){
19559                     if(proc.el != el){
19560                         startProc(el, "up");
19561                     }
19562                     return;
19563                 }else if(pt.x - r.left <= dds.thresh){
19564                     if(proc.el != el){
19565                         startProc(el, "right");
19566                     }
19567                     return;
19568                 }
19569             }
19570         }
19571         clearProc();
19572     };
19573     
19574     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19575     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19576     
19577     return {
19578         /**
19579          * Registers new overflow element(s) to auto scroll
19580          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19581          */
19582         register : function(el){
19583             if(el instanceof Array){
19584                 for(var i = 0, len = el.length; i < len; i++) {
19585                         this.register(el[i]);
19586                 }
19587             }else{
19588                 el = Roo.get(el);
19589                 els[el.id] = el;
19590             }
19591             Roo.dd.ScrollManager.els = els;
19592         },
19593         
19594         /**
19595          * Unregisters overflow element(s) so they are no longer scrolled
19596          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19597          */
19598         unregister : function(el){
19599             if(el instanceof Array){
19600                 for(var i = 0, len = el.length; i < len; i++) {
19601                         this.unregister(el[i]);
19602                 }
19603             }else{
19604                 el = Roo.get(el);
19605                 delete els[el.id];
19606             }
19607         },
19608         
19609         /**
19610          * The number of pixels from the edge of a container the pointer needs to be to 
19611          * trigger scrolling (defaults to 25)
19612          * @type Number
19613          */
19614         thresh : 25,
19615         
19616         /**
19617          * The number of pixels to scroll in each scroll increment (defaults to 50)
19618          * @type Number
19619          */
19620         increment : 100,
19621         
19622         /**
19623          * The frequency of scrolls in milliseconds (defaults to 500)
19624          * @type Number
19625          */
19626         frequency : 500,
19627         
19628         /**
19629          * True to animate the scroll (defaults to true)
19630          * @type Boolean
19631          */
19632         animate: true,
19633         
19634         /**
19635          * The animation duration in seconds - 
19636          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19637          * @type Number
19638          */
19639         animDuration: .4,
19640         
19641         /**
19642          * Manually trigger a cache refresh.
19643          */
19644         refreshCache : function(){
19645             for(var id in els){
19646                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19647                     els[id]._region = els[id].getRegion();
19648                 }
19649             }
19650         }
19651     };
19652 }();/*
19653  * Based on:
19654  * Ext JS Library 1.1.1
19655  * Copyright(c) 2006-2007, Ext JS, LLC.
19656  *
19657  * Originally Released Under LGPL - original licence link has changed is not relivant.
19658  *
19659  * Fork - LGPL
19660  * <script type="text/javascript">
19661  */
19662  
19663
19664 /**
19665  * @class Roo.dd.Registry
19666  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19667  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19668  * @singleton
19669  */
19670 Roo.dd.Registry = function(){
19671     var elements = {}; 
19672     var handles = {}; 
19673     var autoIdSeed = 0;
19674
19675     var getId = function(el, autogen){
19676         if(typeof el == "string"){
19677             return el;
19678         }
19679         var id = el.id;
19680         if(!id && autogen !== false){
19681             id = "roodd-" + (++autoIdSeed);
19682             el.id = id;
19683         }
19684         return id;
19685     };
19686     
19687     return {
19688     /**
19689      * Register a drag drop element
19690      * @param {String|HTMLElement} element The id or DOM node to register
19691      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19692      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19693      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19694      * populated in the data object (if applicable):
19695      * <pre>
19696 Value      Description<br />
19697 ---------  ------------------------------------------<br />
19698 handles    Array of DOM nodes that trigger dragging<br />
19699            for the element being registered<br />
19700 isHandle   True if the element passed in triggers<br />
19701            dragging itself, else false
19702 </pre>
19703      */
19704         register : function(el, data){
19705             data = data || {};
19706             if(typeof el == "string"){
19707                 el = document.getElementById(el);
19708             }
19709             data.ddel = el;
19710             elements[getId(el)] = data;
19711             if(data.isHandle !== false){
19712                 handles[data.ddel.id] = data;
19713             }
19714             if(data.handles){
19715                 var hs = data.handles;
19716                 for(var i = 0, len = hs.length; i < len; i++){
19717                         handles[getId(hs[i])] = data;
19718                 }
19719             }
19720         },
19721
19722     /**
19723      * Unregister a drag drop element
19724      * @param {String|HTMLElement}  element The id or DOM node to unregister
19725      */
19726         unregister : function(el){
19727             var id = getId(el, false);
19728             var data = elements[id];
19729             if(data){
19730                 delete elements[id];
19731                 if(data.handles){
19732                     var hs = data.handles;
19733                     for(var i = 0, len = hs.length; i < len; i++){
19734                         delete handles[getId(hs[i], false)];
19735                     }
19736                 }
19737             }
19738         },
19739
19740     /**
19741      * Returns the handle registered for a DOM Node by id
19742      * @param {String|HTMLElement} id The DOM node or id to look up
19743      * @return {Object} handle The custom handle data
19744      */
19745         getHandle : function(id){
19746             if(typeof id != "string"){ // must be element?
19747                 id = id.id;
19748             }
19749             return handles[id];
19750         },
19751
19752     /**
19753      * Returns the handle that is registered for the DOM node that is the target of the event
19754      * @param {Event} e The event
19755      * @return {Object} handle The custom handle data
19756      */
19757         getHandleFromEvent : function(e){
19758             var t = Roo.lib.Event.getTarget(e);
19759             return t ? handles[t.id] : null;
19760         },
19761
19762     /**
19763      * Returns a custom data object that is registered for a DOM node by id
19764      * @param {String|HTMLElement} id The DOM node or id to look up
19765      * @return {Object} data The custom data
19766      */
19767         getTarget : function(id){
19768             if(typeof id != "string"){ // must be element?
19769                 id = id.id;
19770             }
19771             return elements[id];
19772         },
19773
19774     /**
19775      * Returns a custom data object that is registered for the DOM node that is the target of the event
19776      * @param {Event} e The event
19777      * @return {Object} data The custom data
19778      */
19779         getTargetFromEvent : function(e){
19780             var t = Roo.lib.Event.getTarget(e);
19781             return t ? elements[t.id] || handles[t.id] : null;
19782         }
19783     };
19784 }();/*
19785  * Based on:
19786  * Ext JS Library 1.1.1
19787  * Copyright(c) 2006-2007, Ext JS, LLC.
19788  *
19789  * Originally Released Under LGPL - original licence link has changed is not relivant.
19790  *
19791  * Fork - LGPL
19792  * <script type="text/javascript">
19793  */
19794  
19795
19796 /**
19797  * @class Roo.dd.StatusProxy
19798  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19799  * default drag proxy used by all Roo.dd components.
19800  * @constructor
19801  * @param {Object} config
19802  */
19803 Roo.dd.StatusProxy = function(config){
19804     Roo.apply(this, config);
19805     this.id = this.id || Roo.id();
19806     this.el = new Roo.Layer({
19807         dh: {
19808             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19809                 {tag: "div", cls: "x-dd-drop-icon"},
19810                 {tag: "div", cls: "x-dd-drag-ghost"}
19811             ]
19812         }, 
19813         shadow: !config || config.shadow !== false
19814     });
19815     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19816     this.dropStatus = this.dropNotAllowed;
19817 };
19818
19819 Roo.dd.StatusProxy.prototype = {
19820     /**
19821      * @cfg {String} dropAllowed
19822      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19823      */
19824     dropAllowed : "x-dd-drop-ok",
19825     /**
19826      * @cfg {String} dropNotAllowed
19827      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19828      */
19829     dropNotAllowed : "x-dd-drop-nodrop",
19830
19831     /**
19832      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19833      * over the current target element.
19834      * @param {String} cssClass The css class for the new drop status indicator image
19835      */
19836     setStatus : function(cssClass){
19837         cssClass = cssClass || this.dropNotAllowed;
19838         if(this.dropStatus != cssClass){
19839             this.el.replaceClass(this.dropStatus, cssClass);
19840             this.dropStatus = cssClass;
19841         }
19842     },
19843
19844     /**
19845      * Resets the status indicator to the default dropNotAllowed value
19846      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19847      */
19848     reset : function(clearGhost){
19849         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19850         this.dropStatus = this.dropNotAllowed;
19851         if(clearGhost){
19852             this.ghost.update("");
19853         }
19854     },
19855
19856     /**
19857      * Updates the contents of the ghost element
19858      * @param {String} html The html that will replace the current innerHTML of the ghost element
19859      */
19860     update : function(html){
19861         if(typeof html == "string"){
19862             this.ghost.update(html);
19863         }else{
19864             this.ghost.update("");
19865             html.style.margin = "0";
19866             this.ghost.dom.appendChild(html);
19867         }
19868         // ensure float = none set?? cant remember why though.
19869         var el = this.ghost.dom.firstChild;
19870                 if(el){
19871                         Roo.fly(el).setStyle('float', 'none');
19872                 }
19873     },
19874     
19875     /**
19876      * Returns the underlying proxy {@link Roo.Layer}
19877      * @return {Roo.Layer} el
19878     */
19879     getEl : function(){
19880         return this.el;
19881     },
19882
19883     /**
19884      * Returns the ghost element
19885      * @return {Roo.Element} el
19886      */
19887     getGhost : function(){
19888         return this.ghost;
19889     },
19890
19891     /**
19892      * Hides the proxy
19893      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19894      */
19895     hide : function(clear){
19896         this.el.hide();
19897         if(clear){
19898             this.reset(true);
19899         }
19900     },
19901
19902     /**
19903      * Stops the repair animation if it's currently running
19904      */
19905     stop : function(){
19906         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19907             this.anim.stop();
19908         }
19909     },
19910
19911     /**
19912      * Displays this proxy
19913      */
19914     show : function(){
19915         this.el.show();
19916     },
19917
19918     /**
19919      * Force the Layer to sync its shadow and shim positions to the element
19920      */
19921     sync : function(){
19922         this.el.sync();
19923     },
19924
19925     /**
19926      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19927      * invalid drop operation by the item being dragged.
19928      * @param {Array} xy The XY position of the element ([x, y])
19929      * @param {Function} callback The function to call after the repair is complete
19930      * @param {Object} scope The scope in which to execute the callback
19931      */
19932     repair : function(xy, callback, scope){
19933         this.callback = callback;
19934         this.scope = scope;
19935         if(xy && this.animRepair !== false){
19936             this.el.addClass("x-dd-drag-repair");
19937             this.el.hideUnders(true);
19938             this.anim = this.el.shift({
19939                 duration: this.repairDuration || .5,
19940                 easing: 'easeOut',
19941                 xy: xy,
19942                 stopFx: true,
19943                 callback: this.afterRepair,
19944                 scope: this
19945             });
19946         }else{
19947             this.afterRepair();
19948         }
19949     },
19950
19951     // private
19952     afterRepair : function(){
19953         this.hide(true);
19954         if(typeof this.callback == "function"){
19955             this.callback.call(this.scope || this);
19956         }
19957         this.callback = null;
19958         this.scope = null;
19959     }
19960 };/*
19961  * Based on:
19962  * Ext JS Library 1.1.1
19963  * Copyright(c) 2006-2007, Ext JS, LLC.
19964  *
19965  * Originally Released Under LGPL - original licence link has changed is not relivant.
19966  *
19967  * Fork - LGPL
19968  * <script type="text/javascript">
19969  */
19970
19971 /**
19972  * @class Roo.dd.DragSource
19973  * @extends Roo.dd.DDProxy
19974  * A simple class that provides the basic implementation needed to make any element draggable.
19975  * @constructor
19976  * @param {String/HTMLElement/Element} el The container element
19977  * @param {Object} config
19978  */
19979 Roo.dd.DragSource = function(el, config){
19980     this.el = Roo.get(el);
19981     this.dragData = {};
19982     
19983     Roo.apply(this, config);
19984     
19985     if(!this.proxy){
19986         this.proxy = new Roo.dd.StatusProxy();
19987     }
19988
19989     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19990           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19991     
19992     this.dragging = false;
19993 };
19994
19995 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19996     /**
19997      * @cfg {String} dropAllowed
19998      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19999      */
20000     dropAllowed : "x-dd-drop-ok",
20001     /**
20002      * @cfg {String} dropNotAllowed
20003      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20004      */
20005     dropNotAllowed : "x-dd-drop-nodrop",
20006
20007     /**
20008      * Returns the data object associated with this drag source
20009      * @return {Object} data An object containing arbitrary data
20010      */
20011     getDragData : function(e){
20012         return this.dragData;
20013     },
20014
20015     // private
20016     onDragEnter : function(e, id){
20017         var target = Roo.dd.DragDropMgr.getDDById(id);
20018         this.cachedTarget = target;
20019         if(this.beforeDragEnter(target, e, id) !== false){
20020             if(target.isNotifyTarget){
20021                 var status = target.notifyEnter(this, e, this.dragData);
20022                 this.proxy.setStatus(status);
20023             }else{
20024                 this.proxy.setStatus(this.dropAllowed);
20025             }
20026             
20027             if(this.afterDragEnter){
20028                 /**
20029                  * An empty function by default, but provided so that you can perform a custom action
20030                  * when the dragged item enters the drop target by providing an implementation.
20031                  * @param {Roo.dd.DragDrop} target The drop target
20032                  * @param {Event} e The event object
20033                  * @param {String} id The id of the dragged element
20034                  * @method afterDragEnter
20035                  */
20036                 this.afterDragEnter(target, e, id);
20037             }
20038         }
20039     },
20040
20041     /**
20042      * An empty function by default, but provided so that you can perform a custom action
20043      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20044      * @param {Roo.dd.DragDrop} target The drop target
20045      * @param {Event} e The event object
20046      * @param {String} id The id of the dragged element
20047      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20048      */
20049     beforeDragEnter : function(target, e, id){
20050         return true;
20051     },
20052
20053     // private
20054     alignElWithMouse: function() {
20055         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20056         this.proxy.sync();
20057     },
20058
20059     // private
20060     onDragOver : function(e, id){
20061         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20062         if(this.beforeDragOver(target, e, id) !== false){
20063             if(target.isNotifyTarget){
20064                 var status = target.notifyOver(this, e, this.dragData);
20065                 this.proxy.setStatus(status);
20066             }
20067
20068             if(this.afterDragOver){
20069                 /**
20070                  * An empty function by default, but provided so that you can perform a custom action
20071                  * while the dragged item is over the drop target by providing an implementation.
20072                  * @param {Roo.dd.DragDrop} target The drop target
20073                  * @param {Event} e The event object
20074                  * @param {String} id The id of the dragged element
20075                  * @method afterDragOver
20076                  */
20077                 this.afterDragOver(target, e, id);
20078             }
20079         }
20080     },
20081
20082     /**
20083      * An empty function by default, but provided so that you can perform a custom action
20084      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20085      * @param {Roo.dd.DragDrop} target The drop target
20086      * @param {Event} e The event object
20087      * @param {String} id The id of the dragged element
20088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20089      */
20090     beforeDragOver : function(target, e, id){
20091         return true;
20092     },
20093
20094     // private
20095     onDragOut : function(e, id){
20096         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20097         if(this.beforeDragOut(target, e, id) !== false){
20098             if(target.isNotifyTarget){
20099                 target.notifyOut(this, e, this.dragData);
20100             }
20101             this.proxy.reset();
20102             if(this.afterDragOut){
20103                 /**
20104                  * An empty function by default, but provided so that you can perform a custom action
20105                  * after the dragged item is dragged out of the target without dropping.
20106                  * @param {Roo.dd.DragDrop} target The drop target
20107                  * @param {Event} e The event object
20108                  * @param {String} id The id of the dragged element
20109                  * @method afterDragOut
20110                  */
20111                 this.afterDragOut(target, e, id);
20112             }
20113         }
20114         this.cachedTarget = null;
20115     },
20116
20117     /**
20118      * An empty function by default, but provided so that you can perform a custom action before the dragged
20119      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20120      * @param {Roo.dd.DragDrop} target The drop target
20121      * @param {Event} e The event object
20122      * @param {String} id The id of the dragged element
20123      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20124      */
20125     beforeDragOut : function(target, e, id){
20126         return true;
20127     },
20128     
20129     // private
20130     onDragDrop : function(e, id){
20131         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20132         if(this.beforeDragDrop(target, e, id) !== false){
20133             if(target.isNotifyTarget){
20134                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20135                     this.onValidDrop(target, e, id);
20136                 }else{
20137                     this.onInvalidDrop(target, e, id);
20138                 }
20139             }else{
20140                 this.onValidDrop(target, e, id);
20141             }
20142             
20143             if(this.afterDragDrop){
20144                 /**
20145                  * An empty function by default, but provided so that you can perform a custom action
20146                  * after a valid drag drop has occurred by providing an implementation.
20147                  * @param {Roo.dd.DragDrop} target The drop target
20148                  * @param {Event} e The event object
20149                  * @param {String} id The id of the dropped element
20150                  * @method afterDragDrop
20151                  */
20152                 this.afterDragDrop(target, e, id);
20153             }
20154         }
20155         delete this.cachedTarget;
20156     },
20157
20158     /**
20159      * An empty function by default, but provided so that you can perform a custom action before the dragged
20160      * item is dropped onto the target and optionally cancel the onDragDrop.
20161      * @param {Roo.dd.DragDrop} target The drop target
20162      * @param {Event} e The event object
20163      * @param {String} id The id of the dragged element
20164      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20165      */
20166     beforeDragDrop : function(target, e, id){
20167         return true;
20168     },
20169
20170     // private
20171     onValidDrop : function(target, e, id){
20172         this.hideProxy();
20173         if(this.afterValidDrop){
20174             /**
20175              * An empty function by default, but provided so that you can perform a custom action
20176              * after a valid drop has occurred by providing an implementation.
20177              * @param {Object} target The target DD 
20178              * @param {Event} e The event object
20179              * @param {String} id The id of the dropped element
20180              * @method afterInvalidDrop
20181              */
20182             this.afterValidDrop(target, e, id);
20183         }
20184     },
20185
20186     // private
20187     getRepairXY : function(e, data){
20188         return this.el.getXY();  
20189     },
20190
20191     // private
20192     onInvalidDrop : function(target, e, id){
20193         this.beforeInvalidDrop(target, e, id);
20194         if(this.cachedTarget){
20195             if(this.cachedTarget.isNotifyTarget){
20196                 this.cachedTarget.notifyOut(this, e, this.dragData);
20197             }
20198             this.cacheTarget = null;
20199         }
20200         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20201
20202         if(this.afterInvalidDrop){
20203             /**
20204              * An empty function by default, but provided so that you can perform a custom action
20205              * after an invalid drop has occurred by providing an implementation.
20206              * @param {Event} e The event object
20207              * @param {String} id The id of the dropped element
20208              * @method afterInvalidDrop
20209              */
20210             this.afterInvalidDrop(e, id);
20211         }
20212     },
20213
20214     // private
20215     afterRepair : function(){
20216         if(Roo.enableFx){
20217             this.el.highlight(this.hlColor || "c3daf9");
20218         }
20219         this.dragging = false;
20220     },
20221
20222     /**
20223      * An empty function by default, but provided so that you can perform a custom action after an invalid
20224      * drop has occurred.
20225      * @param {Roo.dd.DragDrop} target The drop target
20226      * @param {Event} e The event object
20227      * @param {String} id The id of the dragged element
20228      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20229      */
20230     beforeInvalidDrop : function(target, e, id){
20231         return true;
20232     },
20233
20234     // private
20235     handleMouseDown : function(e){
20236         if(this.dragging) {
20237             return;
20238         }
20239         var data = this.getDragData(e);
20240         if(data && this.onBeforeDrag(data, e) !== false){
20241             this.dragData = data;
20242             this.proxy.stop();
20243             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20244         } 
20245     },
20246
20247     /**
20248      * An empty function by default, but provided so that you can perform a custom action before the initial
20249      * drag event begins and optionally cancel it.
20250      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20251      * @param {Event} e The event object
20252      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20253      */
20254     onBeforeDrag : function(data, e){
20255         return true;
20256     },
20257
20258     /**
20259      * An empty function by default, but provided so that you can perform a custom action once the initial
20260      * drag event has begun.  The drag cannot be canceled from this function.
20261      * @param {Number} x The x position of the click on the dragged object
20262      * @param {Number} y The y position of the click on the dragged object
20263      */
20264     onStartDrag : Roo.emptyFn,
20265
20266     // private - YUI override
20267     startDrag : function(x, y){
20268         this.proxy.reset();
20269         this.dragging = true;
20270         this.proxy.update("");
20271         this.onInitDrag(x, y);
20272         this.proxy.show();
20273     },
20274
20275     // private
20276     onInitDrag : function(x, y){
20277         var clone = this.el.dom.cloneNode(true);
20278         clone.id = Roo.id(); // prevent duplicate ids
20279         this.proxy.update(clone);
20280         this.onStartDrag(x, y);
20281         return true;
20282     },
20283
20284     /**
20285      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20286      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20287      */
20288     getProxy : function(){
20289         return this.proxy;  
20290     },
20291
20292     /**
20293      * Hides the drag source's {@link Roo.dd.StatusProxy}
20294      */
20295     hideProxy : function(){
20296         this.proxy.hide();  
20297         this.proxy.reset(true);
20298         this.dragging = false;
20299     },
20300
20301     // private
20302     triggerCacheRefresh : function(){
20303         Roo.dd.DDM.refreshCache(this.groups);
20304     },
20305
20306     // private - override to prevent hiding
20307     b4EndDrag: function(e) {
20308     },
20309
20310     // private - override to prevent moving
20311     endDrag : function(e){
20312         this.onEndDrag(this.dragData, e);
20313     },
20314
20315     // private
20316     onEndDrag : function(data, e){
20317     },
20318     
20319     // private - pin to cursor
20320     autoOffset : function(x, y) {
20321         this.setDelta(-12, -20);
20322     }    
20323 });/*
20324  * Based on:
20325  * Ext JS Library 1.1.1
20326  * Copyright(c) 2006-2007, Ext JS, LLC.
20327  *
20328  * Originally Released Under LGPL - original licence link has changed is not relivant.
20329  *
20330  * Fork - LGPL
20331  * <script type="text/javascript">
20332  */
20333
20334
20335 /**
20336  * @class Roo.dd.DropTarget
20337  * @extends Roo.dd.DDTarget
20338  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20339  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20340  * @constructor
20341  * @param {String/HTMLElement/Element} el The container element
20342  * @param {Object} config
20343  */
20344 Roo.dd.DropTarget = function(el, config){
20345     this.el = Roo.get(el);
20346     
20347     var listeners = false; ;
20348     if (config && config.listeners) {
20349         listeners= config.listeners;
20350         delete config.listeners;
20351     }
20352     Roo.apply(this, config);
20353     
20354     if(this.containerScroll){
20355         Roo.dd.ScrollManager.register(this.el);
20356     }
20357     this.addEvents( {
20358          /**
20359          * @scope Roo.dd.DropTarget
20360          */
20361          
20362          /**
20363          * @event enter
20364          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20365          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20366          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20367          * 
20368          * IMPORTANT : it should set this.overClass and this.dropAllowed
20369          * 
20370          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20371          * @param {Event} e The event
20372          * @param {Object} data An object containing arbitrary data supplied by the drag source
20373          */
20374         "enter" : true,
20375         
20376          /**
20377          * @event over
20378          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20379          * This method will be called on every mouse movement while the drag source is over the drop target.
20380          * This default implementation simply returns the dropAllowed config value.
20381          * 
20382          * IMPORTANT : it should set this.dropAllowed
20383          * 
20384          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20385          * @param {Event} e The event
20386          * @param {Object} data An object containing arbitrary data supplied by the drag source
20387          
20388          */
20389         "over" : true,
20390         /**
20391          * @event out
20392          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20393          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20394          * overClass (if any) from the drop element.
20395          * 
20396          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20397          * @param {Event} e The event
20398          * @param {Object} data An object containing arbitrary data supplied by the drag source
20399          */
20400          "out" : true,
20401          
20402         /**
20403          * @event drop
20404          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20405          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20406          * implementation that does something to process the drop event and returns true so that the drag source's
20407          * repair action does not run.
20408          * 
20409          * IMPORTANT : it should set this.success
20410          * 
20411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20412          * @param {Event} e The event
20413          * @param {Object} data An object containing arbitrary data supplied by the drag source
20414         */
20415          "drop" : true
20416     });
20417             
20418      
20419     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20420         this.el.dom, 
20421         this.ddGroup || this.group,
20422         {
20423             isTarget: true,
20424             listeners : listeners || {} 
20425            
20426         
20427         }
20428     );
20429
20430 };
20431
20432 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20433     /**
20434      * @cfg {String} overClass
20435      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20436      */
20437      /**
20438      * @cfg {String} ddGroup
20439      * The drag drop group to handle drop events for
20440      */
20441      
20442     /**
20443      * @cfg {String} dropAllowed
20444      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20445      */
20446     dropAllowed : "x-dd-drop-ok",
20447     /**
20448      * @cfg {String} dropNotAllowed
20449      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20450      */
20451     dropNotAllowed : "x-dd-drop-nodrop",
20452     /**
20453      * @cfg {boolean} success
20454      * set this after drop listener.. 
20455      */
20456     success : false,
20457     /**
20458      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20459      * if the drop point is valid for over/enter..
20460      */
20461     valid : false,
20462     // private
20463     isTarget : true,
20464
20465     // private
20466     isNotifyTarget : true,
20467     
20468     /**
20469      * @hide
20470      */
20471     notifyEnter : function(dd, e, data)
20472     {
20473         this.valid = true;
20474         this.fireEvent('enter', dd, e, data);
20475         if(this.overClass){
20476             this.el.addClass(this.overClass);
20477         }
20478         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20479             this.valid ? this.dropAllowed : this.dropNotAllowed
20480         );
20481     },
20482
20483     /**
20484      * @hide
20485      */
20486     notifyOver : function(dd, e, data)
20487     {
20488         this.valid = true;
20489         this.fireEvent('over', dd, e, data);
20490         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20491             this.valid ? this.dropAllowed : this.dropNotAllowed
20492         );
20493     },
20494
20495     /**
20496      * @hide
20497      */
20498     notifyOut : function(dd, e, data)
20499     {
20500         this.fireEvent('out', dd, e, data);
20501         if(this.overClass){
20502             this.el.removeClass(this.overClass);
20503         }
20504     },
20505
20506     /**
20507      * @hide
20508      */
20509     notifyDrop : function(dd, e, data)
20510     {
20511         this.success = false;
20512         this.fireEvent('drop', dd, e, data);
20513         return this.success;
20514     }
20515 });/*
20516  * Based on:
20517  * Ext JS Library 1.1.1
20518  * Copyright(c) 2006-2007, Ext JS, LLC.
20519  *
20520  * Originally Released Under LGPL - original licence link has changed is not relivant.
20521  *
20522  * Fork - LGPL
20523  * <script type="text/javascript">
20524  */
20525
20526
20527 /**
20528  * @class Roo.dd.DragZone
20529  * @extends Roo.dd.DragSource
20530  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20531  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20532  * @constructor
20533  * @param {String/HTMLElement/Element} el The container element
20534  * @param {Object} config
20535  */
20536 Roo.dd.DragZone = function(el, config){
20537     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20538     if(this.containerScroll){
20539         Roo.dd.ScrollManager.register(this.el);
20540     }
20541 };
20542
20543 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20544     /**
20545      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20546      * for auto scrolling during drag operations.
20547      */
20548     /**
20549      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20550      * method after a failed drop (defaults to "c3daf9" - light blue)
20551      */
20552
20553     /**
20554      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20555      * for a valid target to drag based on the mouse down. Override this method
20556      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20557      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20558      * @param {EventObject} e The mouse down event
20559      * @return {Object} The dragData
20560      */
20561     getDragData : function(e){
20562         return Roo.dd.Registry.getHandleFromEvent(e);
20563     },
20564     
20565     /**
20566      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20567      * this.dragData.ddel
20568      * @param {Number} x The x position of the click on the dragged object
20569      * @param {Number} y The y position of the click on the dragged object
20570      * @return {Boolean} true to continue the drag, false to cancel
20571      */
20572     onInitDrag : function(x, y){
20573         this.proxy.update(this.dragData.ddel.cloneNode(true));
20574         this.onStartDrag(x, y);
20575         return true;
20576     },
20577     
20578     /**
20579      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20580      */
20581     afterRepair : function(){
20582         if(Roo.enableFx){
20583             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20584         }
20585         this.dragging = false;
20586     },
20587
20588     /**
20589      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20590      * the XY of this.dragData.ddel
20591      * @param {EventObject} e The mouse up event
20592      * @return {Array} The xy location (e.g. [100, 200])
20593      */
20594     getRepairXY : function(e){
20595         return Roo.Element.fly(this.dragData.ddel).getXY();  
20596     }
20597 });/*
20598  * Based on:
20599  * Ext JS Library 1.1.1
20600  * Copyright(c) 2006-2007, Ext JS, LLC.
20601  *
20602  * Originally Released Under LGPL - original licence link has changed is not relivant.
20603  *
20604  * Fork - LGPL
20605  * <script type="text/javascript">
20606  */
20607 /**
20608  * @class Roo.dd.DropZone
20609  * @extends Roo.dd.DropTarget
20610  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20611  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20612  * @constructor
20613  * @param {String/HTMLElement/Element} el The container element
20614  * @param {Object} config
20615  */
20616 Roo.dd.DropZone = function(el, config){
20617     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20618 };
20619
20620 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20621     /**
20622      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20623      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20624      * provide your own custom lookup.
20625      * @param {Event} e The event
20626      * @return {Object} data The custom data
20627      */
20628     getTargetFromEvent : function(e){
20629         return Roo.dd.Registry.getTargetFromEvent(e);
20630     },
20631
20632     /**
20633      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20634      * that it has registered.  This method has no default implementation and should be overridden to provide
20635      * node-specific processing if necessary.
20636      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20637      * {@link #getTargetFromEvent} for this node)
20638      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20639      * @param {Event} e The event
20640      * @param {Object} data An object containing arbitrary data supplied by the drag source
20641      */
20642     onNodeEnter : function(n, dd, e, data){
20643         
20644     },
20645
20646     /**
20647      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20648      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20649      * overridden to provide the proper feedback.
20650      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20651      * {@link #getTargetFromEvent} for this node)
20652      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20653      * @param {Event} e The event
20654      * @param {Object} data An object containing arbitrary data supplied by the drag source
20655      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20656      * underlying {@link Roo.dd.StatusProxy} can be updated
20657      */
20658     onNodeOver : function(n, dd, e, data){
20659         return this.dropAllowed;
20660     },
20661
20662     /**
20663      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20664      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20665      * node-specific processing if necessary.
20666      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20667      * {@link #getTargetFromEvent} for this node)
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      */
20672     onNodeOut : function(n, dd, e, data){
20673         
20674     },
20675
20676     /**
20677      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20678      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20679      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20680      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20681      * {@link #getTargetFromEvent} for this node)
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {Boolean} True if the drop was valid, else false
20686      */
20687     onNodeDrop : function(n, dd, e, data){
20688         return false;
20689     },
20690
20691     /**
20692      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20693      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20694      * it should be overridden to provide the proper feedback if necessary.
20695      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20696      * @param {Event} e The event
20697      * @param {Object} data An object containing arbitrary data supplied by the drag source
20698      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20699      * underlying {@link Roo.dd.StatusProxy} can be updated
20700      */
20701     onContainerOver : function(dd, e, data){
20702         return this.dropNotAllowed;
20703     },
20704
20705     /**
20706      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20707      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20708      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20709      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20710      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20711      * @param {Event} e The event
20712      * @param {Object} data An object containing arbitrary data supplied by the drag source
20713      * @return {Boolean} True if the drop was valid, else false
20714      */
20715     onContainerDrop : function(dd, e, data){
20716         return false;
20717     },
20718
20719     /**
20720      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20721      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20722      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20723      * you should override this method and provide a custom implementation.
20724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20725      * @param {Event} e The event
20726      * @param {Object} data An object containing arbitrary data supplied by the drag source
20727      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20728      * underlying {@link Roo.dd.StatusProxy} can be updated
20729      */
20730     notifyEnter : function(dd, e, data){
20731         return this.dropNotAllowed;
20732     },
20733
20734     /**
20735      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20736      * This method will be called on every mouse movement while the drag source is over the drop zone.
20737      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20738      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20739      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20740      * registered node, it will call {@link #onContainerOver}.
20741      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20742      * @param {Event} e The event
20743      * @param {Object} data An object containing arbitrary data supplied by the drag source
20744      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20745      * underlying {@link Roo.dd.StatusProxy} can be updated
20746      */
20747     notifyOver : function(dd, e, data){
20748         var n = this.getTargetFromEvent(e);
20749         if(!n){ // not over valid drop target
20750             if(this.lastOverNode){
20751                 this.onNodeOut(this.lastOverNode, dd, e, data);
20752                 this.lastOverNode = null;
20753             }
20754             return this.onContainerOver(dd, e, data);
20755         }
20756         if(this.lastOverNode != n){
20757             if(this.lastOverNode){
20758                 this.onNodeOut(this.lastOverNode, dd, e, data);
20759             }
20760             this.onNodeEnter(n, dd, e, data);
20761             this.lastOverNode = n;
20762         }
20763         return this.onNodeOver(n, dd, e, data);
20764     },
20765
20766     /**
20767      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20768      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20769      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20770      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20771      * @param {Event} e The event
20772      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20773      */
20774     notifyOut : function(dd, e, data){
20775         if(this.lastOverNode){
20776             this.onNodeOut(this.lastOverNode, dd, e, data);
20777             this.lastOverNode = null;
20778         }
20779     },
20780
20781     /**
20782      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20783      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20784      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20785      * otherwise it will call {@link #onContainerDrop}.
20786      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20787      * @param {Event} e The event
20788      * @param {Object} data An object containing arbitrary data supplied by the drag source
20789      * @return {Boolean} True if the drop was valid, else false
20790      */
20791     notifyDrop : function(dd, e, data){
20792         if(this.lastOverNode){
20793             this.onNodeOut(this.lastOverNode, dd, e, data);
20794             this.lastOverNode = null;
20795         }
20796         var n = this.getTargetFromEvent(e);
20797         return n ?
20798             this.onNodeDrop(n, dd, e, data) :
20799             this.onContainerDrop(dd, e, data);
20800     },
20801
20802     // private
20803     triggerCacheRefresh : function(){
20804         Roo.dd.DDM.refreshCache(this.groups);
20805     }  
20806 });/*
20807  * Based on:
20808  * Ext JS Library 1.1.1
20809  * Copyright(c) 2006-2007, Ext JS, LLC.
20810  *
20811  * Originally Released Under LGPL - original licence link has changed is not relivant.
20812  *
20813  * Fork - LGPL
20814  * <script type="text/javascript">
20815  */
20816
20817
20818 /**
20819  * @class Roo.data.SortTypes
20820  * @singleton
20821  * Defines the default sorting (casting?) comparison functions used when sorting data.
20822  */
20823 Roo.data.SortTypes = {
20824     /**
20825      * Default sort that does nothing
20826      * @param {Mixed} s The value being converted
20827      * @return {Mixed} The comparison value
20828      */
20829     none : function(s){
20830         return s;
20831     },
20832     
20833     /**
20834      * The regular expression used to strip tags
20835      * @type {RegExp}
20836      * @property
20837      */
20838     stripTagsRE : /<\/?[^>]+>/gi,
20839     
20840     /**
20841      * Strips all HTML tags to sort on text only
20842      * @param {Mixed} s The value being converted
20843      * @return {String} The comparison value
20844      */
20845     asText : function(s){
20846         return String(s).replace(this.stripTagsRE, "");
20847     },
20848     
20849     /**
20850      * Strips all HTML tags to sort on text only - Case insensitive
20851      * @param {Mixed} s The value being converted
20852      * @return {String} The comparison value
20853      */
20854     asUCText : function(s){
20855         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20856     },
20857     
20858     /**
20859      * Case insensitive string
20860      * @param {Mixed} s The value being converted
20861      * @return {String} The comparison value
20862      */
20863     asUCString : function(s) {
20864         return String(s).toUpperCase();
20865     },
20866     
20867     /**
20868      * Date sorting
20869      * @param {Mixed} s The value being converted
20870      * @return {Number} The comparison value
20871      */
20872     asDate : function(s) {
20873         if(!s){
20874             return 0;
20875         }
20876         if(s instanceof Date){
20877             return s.getTime();
20878         }
20879         return Date.parse(String(s));
20880     },
20881     
20882     /**
20883      * Float sorting
20884      * @param {Mixed} s The value being converted
20885      * @return {Float} The comparison value
20886      */
20887     asFloat : function(s) {
20888         var val = parseFloat(String(s).replace(/,/g, ""));
20889         if(isNaN(val)) val = 0;
20890         return val;
20891     },
20892     
20893     /**
20894      * Integer sorting
20895      * @param {Mixed} s The value being converted
20896      * @return {Number} The comparison value
20897      */
20898     asInt : function(s) {
20899         var val = parseInt(String(s).replace(/,/g, ""));
20900         if(isNaN(val)) val = 0;
20901         return val;
20902     }
20903 };/*
20904  * Based on:
20905  * Ext JS Library 1.1.1
20906  * Copyright(c) 2006-2007, Ext JS, LLC.
20907  *
20908  * Originally Released Under LGPL - original licence link has changed is not relivant.
20909  *
20910  * Fork - LGPL
20911  * <script type="text/javascript">
20912  */
20913
20914 /**
20915 * @class Roo.data.Record
20916  * Instances of this class encapsulate both record <em>definition</em> information, and record
20917  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20918  * to access Records cached in an {@link Roo.data.Store} object.<br>
20919  * <p>
20920  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20921  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20922  * objects.<br>
20923  * <p>
20924  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20925  * @constructor
20926  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20927  * {@link #create}. The parameters are the same.
20928  * @param {Array} data An associative Array of data values keyed by the field name.
20929  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20930  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20931  * not specified an integer id is generated.
20932  */
20933 Roo.data.Record = function(data, id){
20934     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20935     this.data = data;
20936 };
20937
20938 /**
20939  * Generate a constructor for a specific record layout.
20940  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20941  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20942  * Each field definition object may contain the following properties: <ul>
20943  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20944  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20945  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20946  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20947  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20948  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20949  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20950  * this may be omitted.</p></li>
20951  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20952  * <ul><li>auto (Default, implies no conversion)</li>
20953  * <li>string</li>
20954  * <li>int</li>
20955  * <li>float</li>
20956  * <li>boolean</li>
20957  * <li>date</li></ul></p></li>
20958  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20959  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20960  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20961  * by the Reader into an object that will be stored in the Record. It is passed the
20962  * following parameters:<ul>
20963  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20964  * </ul></p></li>
20965  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20966  * </ul>
20967  * <br>usage:<br><pre><code>
20968 var TopicRecord = Roo.data.Record.create(
20969     {name: 'title', mapping: 'topic_title'},
20970     {name: 'author', mapping: 'username'},
20971     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20972     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20973     {name: 'lastPoster', mapping: 'user2'},
20974     {name: 'excerpt', mapping: 'post_text'}
20975 );
20976
20977 var myNewRecord = new TopicRecord({
20978     title: 'Do my job please',
20979     author: 'noobie',
20980     totalPosts: 1,
20981     lastPost: new Date(),
20982     lastPoster: 'Animal',
20983     excerpt: 'No way dude!'
20984 });
20985 myStore.add(myNewRecord);
20986 </code></pre>
20987  * @method create
20988  * @static
20989  */
20990 Roo.data.Record.create = function(o){
20991     var f = function(){
20992         f.superclass.constructor.apply(this, arguments);
20993     };
20994     Roo.extend(f, Roo.data.Record);
20995     var p = f.prototype;
20996     p.fields = new Roo.util.MixedCollection(false, function(field){
20997         return field.name;
20998     });
20999     for(var i = 0, len = o.length; i < len; i++){
21000         p.fields.add(new Roo.data.Field(o[i]));
21001     }
21002     f.getField = function(name){
21003         return p.fields.get(name);  
21004     };
21005     return f;
21006 };
21007
21008 Roo.data.Record.AUTO_ID = 1000;
21009 Roo.data.Record.EDIT = 'edit';
21010 Roo.data.Record.REJECT = 'reject';
21011 Roo.data.Record.COMMIT = 'commit';
21012
21013 Roo.data.Record.prototype = {
21014     /**
21015      * Readonly flag - true if this record has been modified.
21016      * @type Boolean
21017      */
21018     dirty : false,
21019     editing : false,
21020     error: null,
21021     modified: null,
21022
21023     // private
21024     join : function(store){
21025         this.store = store;
21026     },
21027
21028     /**
21029      * Set the named field to the specified value.
21030      * @param {String} name The name of the field to set.
21031      * @param {Object} value The value to set the field to.
21032      */
21033     set : function(name, value){
21034         if(this.data[name] == value){
21035             return;
21036         }
21037         this.dirty = true;
21038         if(!this.modified){
21039             this.modified = {};
21040         }
21041         if(typeof this.modified[name] == 'undefined'){
21042             this.modified[name] = this.data[name];
21043         }
21044         this.data[name] = value;
21045         if(!this.editing && this.store){
21046             this.store.afterEdit(this);
21047         }       
21048     },
21049
21050     /**
21051      * Get the value of the named field.
21052      * @param {String} name The name of the field to get the value of.
21053      * @return {Object} The value of the field.
21054      */
21055     get : function(name){
21056         return this.data[name]; 
21057     },
21058
21059     // private
21060     beginEdit : function(){
21061         this.editing = true;
21062         this.modified = {}; 
21063     },
21064
21065     // private
21066     cancelEdit : function(){
21067         this.editing = false;
21068         delete this.modified;
21069     },
21070
21071     // private
21072     endEdit : function(){
21073         this.editing = false;
21074         if(this.dirty && this.store){
21075             this.store.afterEdit(this);
21076         }
21077     },
21078
21079     /**
21080      * Usually called by the {@link Roo.data.Store} which owns the Record.
21081      * Rejects all changes made to the Record since either creation, or the last commit operation.
21082      * Modified fields are reverted to their original values.
21083      * <p>
21084      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21085      * of reject operations.
21086      */
21087     reject : function(){
21088         var m = this.modified;
21089         for(var n in m){
21090             if(typeof m[n] != "function"){
21091                 this.data[n] = m[n];
21092             }
21093         }
21094         this.dirty = false;
21095         delete this.modified;
21096         this.editing = false;
21097         if(this.store){
21098             this.store.afterReject(this);
21099         }
21100     },
21101
21102     /**
21103      * Usually called by the {@link Roo.data.Store} which owns the Record.
21104      * Commits all changes made to the Record since either creation, or the last commit operation.
21105      * <p>
21106      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21107      * of commit operations.
21108      */
21109     commit : function(){
21110         this.dirty = false;
21111         delete this.modified;
21112         this.editing = false;
21113         if(this.store){
21114             this.store.afterCommit(this);
21115         }
21116     },
21117
21118     // private
21119     hasError : function(){
21120         return this.error != null;
21121     },
21122
21123     // private
21124     clearError : function(){
21125         this.error = null;
21126     },
21127
21128     /**
21129      * Creates a copy of this record.
21130      * @param {String} id (optional) A new record id if you don't want to use this record's id
21131      * @return {Record}
21132      */
21133     copy : function(newId) {
21134         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21135     }
21136 };/*
21137  * Based on:
21138  * Ext JS Library 1.1.1
21139  * Copyright(c) 2006-2007, Ext JS, LLC.
21140  *
21141  * Originally Released Under LGPL - original licence link has changed is not relivant.
21142  *
21143  * Fork - LGPL
21144  * <script type="text/javascript">
21145  */
21146
21147
21148
21149 /**
21150  * @class Roo.data.Store
21151  * @extends Roo.util.Observable
21152  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21153  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21154  * <p>
21155  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21156  * has no knowledge of the format of the data returned by the Proxy.<br>
21157  * <p>
21158  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21159  * instances from the data object. These records are cached and made available through accessor functions.
21160  * @constructor
21161  * Creates a new Store.
21162  * @param {Object} config A config object containing the objects needed for the Store to access data,
21163  * and read the data into Records.
21164  */
21165 Roo.data.Store = function(config){
21166     this.data = new Roo.util.MixedCollection(false);
21167     this.data.getKey = function(o){
21168         return o.id;
21169     };
21170     this.baseParams = {};
21171     // private
21172     this.paramNames = {
21173         "start" : "start",
21174         "limit" : "limit",
21175         "sort" : "sort",
21176         "dir" : "dir",
21177         "multisort" : "_multisort"
21178     };
21179
21180     if(config && config.data){
21181         this.inlineData = config.data;
21182         delete config.data;
21183     }
21184
21185     Roo.apply(this, config);
21186     
21187     if(this.reader){ // reader passed
21188         this.reader = Roo.factory(this.reader, Roo.data);
21189         this.reader.xmodule = this.xmodule || false;
21190         if(!this.recordType){
21191             this.recordType = this.reader.recordType;
21192         }
21193         if(this.reader.onMetaChange){
21194             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21195         }
21196     }
21197
21198     if(this.recordType){
21199         this.fields = this.recordType.prototype.fields;
21200     }
21201     this.modified = [];
21202
21203     this.addEvents({
21204         /**
21205          * @event datachanged
21206          * Fires when the data cache has changed, and a widget which is using this Store
21207          * as a Record cache should refresh its view.
21208          * @param {Store} this
21209          */
21210         datachanged : true,
21211         /**
21212          * @event metachange
21213          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21214          * @param {Store} this
21215          * @param {Object} meta The JSON metadata
21216          */
21217         metachange : true,
21218         /**
21219          * @event add
21220          * Fires when Records have been added to the Store
21221          * @param {Store} this
21222          * @param {Roo.data.Record[]} records The array of Records added
21223          * @param {Number} index The index at which the record(s) were added
21224          */
21225         add : true,
21226         /**
21227          * @event remove
21228          * Fires when a Record has been removed from the Store
21229          * @param {Store} this
21230          * @param {Roo.data.Record} record The Record that was removed
21231          * @param {Number} index The index at which the record was removed
21232          */
21233         remove : true,
21234         /**
21235          * @event update
21236          * Fires when a Record has been updated
21237          * @param {Store} this
21238          * @param {Roo.data.Record} record The Record that was updated
21239          * @param {String} operation The update operation being performed.  Value may be one of:
21240          * <pre><code>
21241  Roo.data.Record.EDIT
21242  Roo.data.Record.REJECT
21243  Roo.data.Record.COMMIT
21244          * </code></pre>
21245          */
21246         update : true,
21247         /**
21248          * @event clear
21249          * Fires when the data cache has been cleared.
21250          * @param {Store} this
21251          */
21252         clear : true,
21253         /**
21254          * @event beforeload
21255          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21256          * the load action will be canceled.
21257          * @param {Store} this
21258          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21259          */
21260         beforeload : true,
21261         /**
21262          * @event beforeloadadd
21263          * Fires after a new set of Records has been loaded.
21264          * @param {Store} this
21265          * @param {Roo.data.Record[]} records The Records that were loaded
21266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21267          */
21268         beforeloadadd : true,
21269         /**
21270          * @event load
21271          * Fires after a new set of Records has been loaded, before they are added to the store.
21272          * @param {Store} this
21273          * @param {Roo.data.Record[]} records The Records that were loaded
21274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21275          * @params {Object} return from reader
21276          */
21277         load : true,
21278         /**
21279          * @event loadexception
21280          * Fires if an exception occurs in the Proxy during loading.
21281          * Called with the signature of the Proxy's "loadexception" event.
21282          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21283          * 
21284          * @param {Proxy} 
21285          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21286          * @param {Object} load options 
21287          * @param {Object} jsonData from your request (normally this contains the Exception)
21288          */
21289         loadexception : true
21290     });
21291     
21292     if(this.proxy){
21293         this.proxy = Roo.factory(this.proxy, Roo.data);
21294         this.proxy.xmodule = this.xmodule || false;
21295         this.relayEvents(this.proxy,  ["loadexception"]);
21296     }
21297     this.sortToggle = {};
21298     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21299
21300     Roo.data.Store.superclass.constructor.call(this);
21301
21302     if(this.inlineData){
21303         this.loadData(this.inlineData);
21304         delete this.inlineData;
21305     }
21306 };
21307
21308 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21309      /**
21310     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21311     * without a remote query - used by combo/forms at present.
21312     */
21313     
21314     /**
21315     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21316     */
21317     /**
21318     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21319     */
21320     /**
21321     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21322     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21323     */
21324     /**
21325     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21326     * on any HTTP request
21327     */
21328     /**
21329     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21330     */
21331     /**
21332     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21333     */
21334     multiSort: false,
21335     /**
21336     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21337     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21338     */
21339     remoteSort : false,
21340
21341     /**
21342     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21343      * loaded or when a record is removed. (defaults to false).
21344     */
21345     pruneModifiedRecords : false,
21346
21347     // private
21348     lastOptions : null,
21349
21350     /**
21351      * Add Records to the Store and fires the add event.
21352      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21353      */
21354     add : function(records){
21355         records = [].concat(records);
21356         for(var i = 0, len = records.length; i < len; i++){
21357             records[i].join(this);
21358         }
21359         var index = this.data.length;
21360         this.data.addAll(records);
21361         this.fireEvent("add", this, records, index);
21362     },
21363
21364     /**
21365      * Remove a Record from the Store and fires the remove event.
21366      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21367      */
21368     remove : function(record){
21369         var index = this.data.indexOf(record);
21370         this.data.removeAt(index);
21371         if(this.pruneModifiedRecords){
21372             this.modified.remove(record);
21373         }
21374         this.fireEvent("remove", this, record, index);
21375     },
21376
21377     /**
21378      * Remove all Records from the Store and fires the clear event.
21379      */
21380     removeAll : function(){
21381         this.data.clear();
21382         if(this.pruneModifiedRecords){
21383             this.modified = [];
21384         }
21385         this.fireEvent("clear", this);
21386     },
21387
21388     /**
21389      * Inserts Records to the Store at the given index and fires the add event.
21390      * @param {Number} index The start index at which to insert the passed Records.
21391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21392      */
21393     insert : function(index, records){
21394         records = [].concat(records);
21395         for(var i = 0, len = records.length; i < len; i++){
21396             this.data.insert(index, records[i]);
21397             records[i].join(this);
21398         }
21399         this.fireEvent("add", this, records, index);
21400     },
21401
21402     /**
21403      * Get the index within the cache of the passed Record.
21404      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21405      * @return {Number} The index of the passed Record. Returns -1 if not found.
21406      */
21407     indexOf : function(record){
21408         return this.data.indexOf(record);
21409     },
21410
21411     /**
21412      * Get the index within the cache of the Record with the passed id.
21413      * @param {String} id The id of the Record to find.
21414      * @return {Number} The index of the Record. Returns -1 if not found.
21415      */
21416     indexOfId : function(id){
21417         return this.data.indexOfKey(id);
21418     },
21419
21420     /**
21421      * Get the Record with the specified id.
21422      * @param {String} id The id of the Record to find.
21423      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21424      */
21425     getById : function(id){
21426         return this.data.key(id);
21427     },
21428
21429     /**
21430      * Get the Record at the specified index.
21431      * @param {Number} index The index of the Record to find.
21432      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21433      */
21434     getAt : function(index){
21435         return this.data.itemAt(index);
21436     },
21437
21438     /**
21439      * Returns a range of Records between specified indices.
21440      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21441      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21442      * @return {Roo.data.Record[]} An array of Records
21443      */
21444     getRange : function(start, end){
21445         return this.data.getRange(start, end);
21446     },
21447
21448     // private
21449     storeOptions : function(o){
21450         o = Roo.apply({}, o);
21451         delete o.callback;
21452         delete o.scope;
21453         this.lastOptions = o;
21454     },
21455
21456     /**
21457      * Loads the Record cache from the configured Proxy using the configured Reader.
21458      * <p>
21459      * If using remote paging, then the first load call must specify the <em>start</em>
21460      * and <em>limit</em> properties in the options.params property to establish the initial
21461      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21462      * <p>
21463      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21464      * and this call will return before the new data has been loaded. Perform any post-processing
21465      * in a callback function, or in a "load" event handler.</strong>
21466      * <p>
21467      * @param {Object} options An object containing properties which control loading options:<ul>
21468      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21469      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21470      * passed the following arguments:<ul>
21471      * <li>r : Roo.data.Record[]</li>
21472      * <li>options: Options object from the load call</li>
21473      * <li>success: Boolean success indicator</li></ul></li>
21474      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21475      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21476      * </ul>
21477      */
21478     load : function(options){
21479         options = options || {};
21480         if(this.fireEvent("beforeload", this, options) !== false){
21481             this.storeOptions(options);
21482             var p = Roo.apply(options.params || {}, this.baseParams);
21483             // if meta was not loaded from remote source.. try requesting it.
21484             if (!this.reader.metaFromRemote) {
21485                 p._requestMeta = 1;
21486             }
21487             if(this.sortInfo && this.remoteSort){
21488                 var pn = this.paramNames;
21489                 p[pn["sort"]] = this.sortInfo.field;
21490                 p[pn["dir"]] = this.sortInfo.direction;
21491             }
21492             if (this.multiSort) {
21493                 var pn = this.paramNames;
21494                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21495             }
21496             
21497             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21498         }
21499     },
21500
21501     /**
21502      * Reloads the Record cache from the configured Proxy using the configured Reader and
21503      * the options from the last load operation performed.
21504      * @param {Object} options (optional) An object containing properties which may override the options
21505      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21506      * the most recently used options are reused).
21507      */
21508     reload : function(options){
21509         this.load(Roo.applyIf(options||{}, this.lastOptions));
21510     },
21511
21512     // private
21513     // Called as a callback by the Reader during a load operation.
21514     loadRecords : function(o, options, success){
21515         if(!o || success === false){
21516             if(success !== false){
21517                 this.fireEvent("load", this, [], options, o);
21518             }
21519             if(options.callback){
21520                 options.callback.call(options.scope || this, [], options, false);
21521             }
21522             return;
21523         }
21524         // if data returned failure - throw an exception.
21525         if (o.success === false) {
21526             // show a message if no listener is registered.
21527             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21528                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21529             }
21530             // loadmask wil be hooked into this..
21531             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21532             return;
21533         }
21534         var r = o.records, t = o.totalRecords || r.length;
21535         
21536         this.fireEvent("beforeloadadd", this, r, options, o);
21537         
21538         if(!options || options.add !== true){
21539             if(this.pruneModifiedRecords){
21540                 this.modified = [];
21541             }
21542             for(var i = 0, len = r.length; i < len; i++){
21543                 r[i].join(this);
21544             }
21545             if(this.snapshot){
21546                 this.data = this.snapshot;
21547                 delete this.snapshot;
21548             }
21549             this.data.clear();
21550             this.data.addAll(r);
21551             this.totalLength = t;
21552             this.applySort();
21553             this.fireEvent("datachanged", this);
21554         }else{
21555             this.totalLength = Math.max(t, this.data.length+r.length);
21556             this.add(r);
21557         }
21558         this.fireEvent("load", this, r, options, o);
21559         if(options.callback){
21560             options.callback.call(options.scope || this, r, options, true);
21561         }
21562     },
21563
21564
21565     /**
21566      * Loads data from a passed data block. A Reader which understands the format of the data
21567      * must have been configured in the constructor.
21568      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21569      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21570      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21571      */
21572     loadData : function(o, append){
21573         var r = this.reader.readRecords(o);
21574         this.loadRecords(r, {add: append}, true);
21575     },
21576
21577     /**
21578      * Gets the number of cached records.
21579      * <p>
21580      * <em>If using paging, this may not be the total size of the dataset. If the data object
21581      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21582      * the data set size</em>
21583      */
21584     getCount : function(){
21585         return this.data.length || 0;
21586     },
21587
21588     /**
21589      * Gets the total number of records in the dataset as returned by the server.
21590      * <p>
21591      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21592      * the dataset size</em>
21593      */
21594     getTotalCount : function(){
21595         return this.totalLength || 0;
21596     },
21597
21598     /**
21599      * Returns the sort state of the Store as an object with two properties:
21600      * <pre><code>
21601  field {String} The name of the field by which the Records are sorted
21602  direction {String} The sort order, "ASC" or "DESC"
21603      * </code></pre>
21604      */
21605     getSortState : function(){
21606         return this.sortInfo;
21607     },
21608
21609     // private
21610     applySort : function(){
21611         if(this.sortInfo && !this.remoteSort){
21612             var s = this.sortInfo, f = s.field;
21613             var st = this.fields.get(f).sortType;
21614             var fn = function(r1, r2){
21615                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21616                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21617             };
21618             this.data.sort(s.direction, fn);
21619             if(this.snapshot && this.snapshot != this.data){
21620                 this.snapshot.sort(s.direction, fn);
21621             }
21622         }
21623     },
21624
21625     /**
21626      * Sets the default sort column and order to be used by the next load operation.
21627      * @param {String} fieldName The name of the field to sort by.
21628      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21629      */
21630     setDefaultSort : function(field, dir){
21631         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21632     },
21633
21634     /**
21635      * Sort the Records.
21636      * If remote sorting is used, the sort is performed on the server, and the cache is
21637      * reloaded. If local sorting is used, the cache is sorted internally.
21638      * @param {String} fieldName The name of the field to sort by.
21639      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21640      */
21641     sort : function(fieldName, dir){
21642         var f = this.fields.get(fieldName);
21643         if(!dir){
21644             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21645             
21646             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21647                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21648             }else{
21649                 dir = f.sortDir;
21650             }
21651         }
21652         this.sortToggle[f.name] = dir;
21653         this.sortInfo = {field: f.name, direction: dir};
21654         if(!this.remoteSort){
21655             this.applySort();
21656             this.fireEvent("datachanged", this);
21657         }else{
21658             this.load(this.lastOptions);
21659         }
21660     },
21661
21662     /**
21663      * Calls the specified function for each of the Records in the cache.
21664      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21665      * Returning <em>false</em> aborts and exits the iteration.
21666      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21667      */
21668     each : function(fn, scope){
21669         this.data.each(fn, scope);
21670     },
21671
21672     /**
21673      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21674      * (e.g., during paging).
21675      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21676      */
21677     getModifiedRecords : function(){
21678         return this.modified;
21679     },
21680
21681     // private
21682     createFilterFn : function(property, value, anyMatch){
21683         if(!value.exec){ // not a regex
21684             value = String(value);
21685             if(value.length == 0){
21686                 return false;
21687             }
21688             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21689         }
21690         return function(r){
21691             return value.test(r.data[property]);
21692         };
21693     },
21694
21695     /**
21696      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21697      * @param {String} property A field on your records
21698      * @param {Number} start The record index to start at (defaults to 0)
21699      * @param {Number} end The last record index to include (defaults to length - 1)
21700      * @return {Number} The sum
21701      */
21702     sum : function(property, start, end){
21703         var rs = this.data.items, v = 0;
21704         start = start || 0;
21705         end = (end || end === 0) ? end : rs.length-1;
21706
21707         for(var i = start; i <= end; i++){
21708             v += (rs[i].data[property] || 0);
21709         }
21710         return v;
21711     },
21712
21713     /**
21714      * Filter the records by a specified property.
21715      * @param {String} field A field on your records
21716      * @param {String/RegExp} value Either a string that the field
21717      * should start with or a RegExp to test against the field
21718      * @param {Boolean} anyMatch True to match any part not just the beginning
21719      */
21720     filter : function(property, value, anyMatch){
21721         var fn = this.createFilterFn(property, value, anyMatch);
21722         return fn ? this.filterBy(fn) : this.clearFilter();
21723     },
21724
21725     /**
21726      * Filter by a function. The specified function will be called with each
21727      * record in this data source. If the function returns true the record is included,
21728      * otherwise it is filtered.
21729      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21730      * @param {Object} scope (optional) The scope of the function (defaults to this)
21731      */
21732     filterBy : function(fn, scope){
21733         this.snapshot = this.snapshot || this.data;
21734         this.data = this.queryBy(fn, scope||this);
21735         this.fireEvent("datachanged", this);
21736     },
21737
21738     /**
21739      * Query the records by a specified property.
21740      * @param {String} field A field on your records
21741      * @param {String/RegExp} value Either a string that the field
21742      * should start with or a RegExp to test against the field
21743      * @param {Boolean} anyMatch True to match any part not just the beginning
21744      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21745      */
21746     query : function(property, value, anyMatch){
21747         var fn = this.createFilterFn(property, value, anyMatch);
21748         return fn ? this.queryBy(fn) : this.data.clone();
21749     },
21750
21751     /**
21752      * Query by a function. The specified function will be called with each
21753      * record in this data source. If the function returns true the record is included
21754      * in the results.
21755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21756      * @param {Object} scope (optional) The scope of the function (defaults to this)
21757       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21758      **/
21759     queryBy : function(fn, scope){
21760         var data = this.snapshot || this.data;
21761         return data.filterBy(fn, scope||this);
21762     },
21763
21764     /**
21765      * Collects unique values for a particular dataIndex from this store.
21766      * @param {String} dataIndex The property to collect
21767      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21768      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21769      * @return {Array} An array of the unique values
21770      **/
21771     collect : function(dataIndex, allowNull, bypassFilter){
21772         var d = (bypassFilter === true && this.snapshot) ?
21773                 this.snapshot.items : this.data.items;
21774         var v, sv, r = [], l = {};
21775         for(var i = 0, len = d.length; i < len; i++){
21776             v = d[i].data[dataIndex];
21777             sv = String(v);
21778             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21779                 l[sv] = true;
21780                 r[r.length] = v;
21781             }
21782         }
21783         return r;
21784     },
21785
21786     /**
21787      * Revert to a view of the Record cache with no filtering applied.
21788      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21789      */
21790     clearFilter : function(suppressEvent){
21791         if(this.snapshot && this.snapshot != this.data){
21792             this.data = this.snapshot;
21793             delete this.snapshot;
21794             if(suppressEvent !== true){
21795                 this.fireEvent("datachanged", this);
21796             }
21797         }
21798     },
21799
21800     // private
21801     afterEdit : function(record){
21802         if(this.modified.indexOf(record) == -1){
21803             this.modified.push(record);
21804         }
21805         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21806     },
21807     
21808     // private
21809     afterReject : function(record){
21810         this.modified.remove(record);
21811         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21812     },
21813
21814     // private
21815     afterCommit : function(record){
21816         this.modified.remove(record);
21817         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21818     },
21819
21820     /**
21821      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21822      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21823      */
21824     commitChanges : function(){
21825         var m = this.modified.slice(0);
21826         this.modified = [];
21827         for(var i = 0, len = m.length; i < len; i++){
21828             m[i].commit();
21829         }
21830     },
21831
21832     /**
21833      * Cancel outstanding changes on all changed records.
21834      */
21835     rejectChanges : function(){
21836         var m = this.modified.slice(0);
21837         this.modified = [];
21838         for(var i = 0, len = m.length; i < len; i++){
21839             m[i].reject();
21840         }
21841     },
21842
21843     onMetaChange : function(meta, rtype, o){
21844         this.recordType = rtype;
21845         this.fields = rtype.prototype.fields;
21846         delete this.snapshot;
21847         this.sortInfo = meta.sortInfo || this.sortInfo;
21848         this.modified = [];
21849         this.fireEvent('metachange', this, this.reader.meta);
21850     },
21851     
21852     moveIndex : function(data, type)
21853     {
21854         var index = this.indexOf(data);
21855         
21856         var newIndex = index + type;
21857         
21858         this.remove(data);
21859         
21860         this.insert(newIndex, data);
21861         
21862     }
21863 });/*
21864  * Based on:
21865  * Ext JS Library 1.1.1
21866  * Copyright(c) 2006-2007, Ext JS, LLC.
21867  *
21868  * Originally Released Under LGPL - original licence link has changed is not relivant.
21869  *
21870  * Fork - LGPL
21871  * <script type="text/javascript">
21872  */
21873
21874 /**
21875  * @class Roo.data.SimpleStore
21876  * @extends Roo.data.Store
21877  * Small helper class to make creating Stores from Array data easier.
21878  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21879  * @cfg {Array} fields An array of field definition objects, or field name strings.
21880  * @cfg {Array} data The multi-dimensional array of data
21881  * @constructor
21882  * @param {Object} config
21883  */
21884 Roo.data.SimpleStore = function(config){
21885     Roo.data.SimpleStore.superclass.constructor.call(this, {
21886         isLocal : true,
21887         reader: new Roo.data.ArrayReader({
21888                 id: config.id
21889             },
21890             Roo.data.Record.create(config.fields)
21891         ),
21892         proxy : new Roo.data.MemoryProxy(config.data)
21893     });
21894     this.load();
21895 };
21896 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21897  * Based on:
21898  * Ext JS Library 1.1.1
21899  * Copyright(c) 2006-2007, Ext JS, LLC.
21900  *
21901  * Originally Released Under LGPL - original licence link has changed is not relivant.
21902  *
21903  * Fork - LGPL
21904  * <script type="text/javascript">
21905  */
21906
21907 /**
21908 /**
21909  * @extends Roo.data.Store
21910  * @class Roo.data.JsonStore
21911  * Small helper class to make creating Stores for JSON data easier. <br/>
21912 <pre><code>
21913 var store = new Roo.data.JsonStore({
21914     url: 'get-images.php',
21915     root: 'images',
21916     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21917 });
21918 </code></pre>
21919  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21920  * JsonReader and HttpProxy (unless inline data is provided).</b>
21921  * @cfg {Array} fields An array of field definition objects, or field name strings.
21922  * @constructor
21923  * @param {Object} config
21924  */
21925 Roo.data.JsonStore = function(c){
21926     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21927         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21928         reader: new Roo.data.JsonReader(c, c.fields)
21929     }));
21930 };
21931 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21932  * Based on:
21933  * Ext JS Library 1.1.1
21934  * Copyright(c) 2006-2007, Ext JS, LLC.
21935  *
21936  * Originally Released Under LGPL - original licence link has changed is not relivant.
21937  *
21938  * Fork - LGPL
21939  * <script type="text/javascript">
21940  */
21941
21942  
21943 Roo.data.Field = function(config){
21944     if(typeof config == "string"){
21945         config = {name: config};
21946     }
21947     Roo.apply(this, config);
21948     
21949     if(!this.type){
21950         this.type = "auto";
21951     }
21952     
21953     var st = Roo.data.SortTypes;
21954     // named sortTypes are supported, here we look them up
21955     if(typeof this.sortType == "string"){
21956         this.sortType = st[this.sortType];
21957     }
21958     
21959     // set default sortType for strings and dates
21960     if(!this.sortType){
21961         switch(this.type){
21962             case "string":
21963                 this.sortType = st.asUCString;
21964                 break;
21965             case "date":
21966                 this.sortType = st.asDate;
21967                 break;
21968             default:
21969                 this.sortType = st.none;
21970         }
21971     }
21972
21973     // define once
21974     var stripRe = /[\$,%]/g;
21975
21976     // prebuilt conversion function for this field, instead of
21977     // switching every time we're reading a value
21978     if(!this.convert){
21979         var cv, dateFormat = this.dateFormat;
21980         switch(this.type){
21981             case "":
21982             case "auto":
21983             case undefined:
21984                 cv = function(v){ return v; };
21985                 break;
21986             case "string":
21987                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21988                 break;
21989             case "int":
21990                 cv = function(v){
21991                     return v !== undefined && v !== null && v !== '' ?
21992                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21993                     };
21994                 break;
21995             case "float":
21996                 cv = function(v){
21997                     return v !== undefined && v !== null && v !== '' ?
21998                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21999                     };
22000                 break;
22001             case "bool":
22002             case "boolean":
22003                 cv = function(v){ return v === true || v === "true" || v == 1; };
22004                 break;
22005             case "date":
22006                 cv = function(v){
22007                     if(!v){
22008                         return '';
22009                     }
22010                     if(v instanceof Date){
22011                         return v;
22012                     }
22013                     if(dateFormat){
22014                         if(dateFormat == "timestamp"){
22015                             return new Date(v*1000);
22016                         }
22017                         return Date.parseDate(v, dateFormat);
22018                     }
22019                     var parsed = Date.parse(v);
22020                     return parsed ? new Date(parsed) : null;
22021                 };
22022              break;
22023             
22024         }
22025         this.convert = cv;
22026     }
22027 };
22028
22029 Roo.data.Field.prototype = {
22030     dateFormat: null,
22031     defaultValue: "",
22032     mapping: null,
22033     sortType : null,
22034     sortDir : "ASC"
22035 };/*
22036  * Based on:
22037  * Ext JS Library 1.1.1
22038  * Copyright(c) 2006-2007, Ext JS, LLC.
22039  *
22040  * Originally Released Under LGPL - original licence link has changed is not relivant.
22041  *
22042  * Fork - LGPL
22043  * <script type="text/javascript">
22044  */
22045  
22046 // Base class for reading structured data from a data source.  This class is intended to be
22047 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22048
22049 /**
22050  * @class Roo.data.DataReader
22051  * Base class for reading structured data from a data source.  This class is intended to be
22052  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22053  */
22054
22055 Roo.data.DataReader = function(meta, recordType){
22056     
22057     this.meta = meta;
22058     
22059     this.recordType = recordType instanceof Array ? 
22060         Roo.data.Record.create(recordType) : recordType;
22061 };
22062
22063 Roo.data.DataReader.prototype = {
22064      /**
22065      * Create an empty record
22066      * @param {Object} data (optional) - overlay some values
22067      * @return {Roo.data.Record} record created.
22068      */
22069     newRow :  function(d) {
22070         var da =  {};
22071         this.recordType.prototype.fields.each(function(c) {
22072             switch( c.type) {
22073                 case 'int' : da[c.name] = 0; break;
22074                 case 'date' : da[c.name] = new Date(); break;
22075                 case 'float' : da[c.name] = 0.0; break;
22076                 case 'boolean' : da[c.name] = false; break;
22077                 default : da[c.name] = ""; break;
22078             }
22079             
22080         });
22081         return new this.recordType(Roo.apply(da, d));
22082     }
22083     
22084 };/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094
22095 /**
22096  * @class Roo.data.DataProxy
22097  * @extends Roo.data.Observable
22098  * This class is an abstract base class for implementations which provide retrieval of
22099  * unformatted data objects.<br>
22100  * <p>
22101  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22102  * (of the appropriate type which knows how to parse the data object) to provide a block of
22103  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22104  * <p>
22105  * Custom implementations must implement the load method as described in
22106  * {@link Roo.data.HttpProxy#load}.
22107  */
22108 Roo.data.DataProxy = function(){
22109     this.addEvents({
22110         /**
22111          * @event beforeload
22112          * Fires before a network request is made to retrieve a data object.
22113          * @param {Object} This DataProxy object.
22114          * @param {Object} params The params parameter to the load function.
22115          */
22116         beforeload : true,
22117         /**
22118          * @event load
22119          * Fires before the load method's callback is called.
22120          * @param {Object} This DataProxy object.
22121          * @param {Object} o The data object.
22122          * @param {Object} arg The callback argument object passed to the load function.
22123          */
22124         load : true,
22125         /**
22126          * @event loadexception
22127          * Fires if an Exception occurs during data retrieval.
22128          * @param {Object} This DataProxy object.
22129          * @param {Object} o The data object.
22130          * @param {Object} arg The callback argument object passed to the load function.
22131          * @param {Object} e The Exception.
22132          */
22133         loadexception : true
22134     });
22135     Roo.data.DataProxy.superclass.constructor.call(this);
22136 };
22137
22138 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22139
22140     /**
22141      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22142      */
22143 /*
22144  * Based on:
22145  * Ext JS Library 1.1.1
22146  * Copyright(c) 2006-2007, Ext JS, LLC.
22147  *
22148  * Originally Released Under LGPL - original licence link has changed is not relivant.
22149  *
22150  * Fork - LGPL
22151  * <script type="text/javascript">
22152  */
22153 /**
22154  * @class Roo.data.MemoryProxy
22155  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22156  * to the Reader when its load method is called.
22157  * @constructor
22158  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22159  */
22160 Roo.data.MemoryProxy = function(data){
22161     if (data.data) {
22162         data = data.data;
22163     }
22164     Roo.data.MemoryProxy.superclass.constructor.call(this);
22165     this.data = data;
22166 };
22167
22168 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22169     /**
22170      * Load data from the requested source (in this case an in-memory
22171      * data object passed to the constructor), read the data object into
22172      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22173      * process that block using the passed callback.
22174      * @param {Object} params This parameter is not used by the MemoryProxy class.
22175      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22176      * object into a block of Roo.data.Records.
22177      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22178      * The function must be passed <ul>
22179      * <li>The Record block object</li>
22180      * <li>The "arg" argument from the load function</li>
22181      * <li>A boolean success indicator</li>
22182      * </ul>
22183      * @param {Object} scope The scope in which to call the callback
22184      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22185      */
22186     load : function(params, reader, callback, scope, arg){
22187         params = params || {};
22188         var result;
22189         try {
22190             result = reader.readRecords(this.data);
22191         }catch(e){
22192             this.fireEvent("loadexception", this, arg, null, e);
22193             callback.call(scope, null, arg, false);
22194             return;
22195         }
22196         callback.call(scope, result, arg, true);
22197     },
22198     
22199     // private
22200     update : function(params, records){
22201         
22202     }
22203 });/*
22204  * Based on:
22205  * Ext JS Library 1.1.1
22206  * Copyright(c) 2006-2007, Ext JS, LLC.
22207  *
22208  * Originally Released Under LGPL - original licence link has changed is not relivant.
22209  *
22210  * Fork - LGPL
22211  * <script type="text/javascript">
22212  */
22213 /**
22214  * @class Roo.data.HttpProxy
22215  * @extends Roo.data.DataProxy
22216  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22217  * configured to reference a certain URL.<br><br>
22218  * <p>
22219  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22220  * from which the running page was served.<br><br>
22221  * <p>
22222  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22223  * <p>
22224  * Be aware that to enable the browser to parse an XML document, the server must set
22225  * the Content-Type header in the HTTP response to "text/xml".
22226  * @constructor
22227  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22228  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22229  * will be used to make the request.
22230  */
22231 Roo.data.HttpProxy = function(conn){
22232     Roo.data.HttpProxy.superclass.constructor.call(this);
22233     // is conn a conn config or a real conn?
22234     this.conn = conn;
22235     this.useAjax = !conn || !conn.events;
22236   
22237 };
22238
22239 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22240     // thse are take from connection...
22241     
22242     /**
22243      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22244      */
22245     /**
22246      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22247      * extra parameters to each request made by this object. (defaults to undefined)
22248      */
22249     /**
22250      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22251      *  to each request made by this object. (defaults to undefined)
22252      */
22253     /**
22254      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22255      */
22256     /**
22257      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22258      */
22259      /**
22260      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22261      * @type Boolean
22262      */
22263   
22264
22265     /**
22266      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22267      * @type Boolean
22268      */
22269     /**
22270      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22271      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22272      * a finer-grained basis than the DataProxy events.
22273      */
22274     getConnection : function(){
22275         return this.useAjax ? Roo.Ajax : this.conn;
22276     },
22277
22278     /**
22279      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22280      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22281      * process that block using the passed callback.
22282      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22283      * for the request to the remote server.
22284      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22285      * object into a block of Roo.data.Records.
22286      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22287      * The function must be passed <ul>
22288      * <li>The Record block object</li>
22289      * <li>The "arg" argument from the load function</li>
22290      * <li>A boolean success indicator</li>
22291      * </ul>
22292      * @param {Object} scope The scope in which to call the callback
22293      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22294      */
22295     load : function(params, reader, callback, scope, arg){
22296         if(this.fireEvent("beforeload", this, params) !== false){
22297             var  o = {
22298                 params : params || {},
22299                 request: {
22300                     callback : callback,
22301                     scope : scope,
22302                     arg : arg
22303                 },
22304                 reader: reader,
22305                 callback : this.loadResponse,
22306                 scope: this
22307             };
22308             if(this.useAjax){
22309                 Roo.applyIf(o, this.conn);
22310                 if(this.activeRequest){
22311                     Roo.Ajax.abort(this.activeRequest);
22312                 }
22313                 this.activeRequest = Roo.Ajax.request(o);
22314             }else{
22315                 this.conn.request(o);
22316             }
22317         }else{
22318             callback.call(scope||this, null, arg, false);
22319         }
22320     },
22321
22322     // private
22323     loadResponse : function(o, success, response){
22324         delete this.activeRequest;
22325         if(!success){
22326             this.fireEvent("loadexception", this, o, response);
22327             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22328             return;
22329         }
22330         var result;
22331         try {
22332             result = o.reader.read(response);
22333         }catch(e){
22334             this.fireEvent("loadexception", this, o, response, e);
22335             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22336             return;
22337         }
22338         
22339         this.fireEvent("load", this, o, o.request.arg);
22340         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22341     },
22342
22343     // private
22344     update : function(dataSet){
22345
22346     },
22347
22348     // private
22349     updateResponse : function(dataSet){
22350
22351     }
22352 });/*
22353  * Based on:
22354  * Ext JS Library 1.1.1
22355  * Copyright(c) 2006-2007, Ext JS, LLC.
22356  *
22357  * Originally Released Under LGPL - original licence link has changed is not relivant.
22358  *
22359  * Fork - LGPL
22360  * <script type="text/javascript">
22361  */
22362
22363 /**
22364  * @class Roo.data.ScriptTagProxy
22365  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22366  * other than the originating domain of the running page.<br><br>
22367  * <p>
22368  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22369  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22370  * <p>
22371  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22372  * source code that is used as the source inside a &lt;script> tag.<br><br>
22373  * <p>
22374  * In order for the browser to process the returned data, the server must wrap the data object
22375  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22376  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22377  * depending on whether the callback name was passed:
22378  * <p>
22379  * <pre><code>
22380 boolean scriptTag = false;
22381 String cb = request.getParameter("callback");
22382 if (cb != null) {
22383     scriptTag = true;
22384     response.setContentType("text/javascript");
22385 } else {
22386     response.setContentType("application/x-json");
22387 }
22388 Writer out = response.getWriter();
22389 if (scriptTag) {
22390     out.write(cb + "(");
22391 }
22392 out.print(dataBlock.toJsonString());
22393 if (scriptTag) {
22394     out.write(");");
22395 }
22396 </pre></code>
22397  *
22398  * @constructor
22399  * @param {Object} config A configuration object.
22400  */
22401 Roo.data.ScriptTagProxy = function(config){
22402     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22403     Roo.apply(this, config);
22404     this.head = document.getElementsByTagName("head")[0];
22405 };
22406
22407 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22408
22409 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22410     /**
22411      * @cfg {String} url The URL from which to request the data object.
22412      */
22413     /**
22414      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22415      */
22416     timeout : 30000,
22417     /**
22418      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22419      * the server the name of the callback function set up by the load call to process the returned data object.
22420      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22421      * javascript output which calls this named function passing the data object as its only parameter.
22422      */
22423     callbackParam : "callback",
22424     /**
22425      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22426      * name to the request.
22427      */
22428     nocache : true,
22429
22430     /**
22431      * Load data from the configured URL, read the data object into
22432      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22433      * process that block using the passed callback.
22434      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22435      * for the request to the remote server.
22436      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22437      * object into a block of Roo.data.Records.
22438      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22439      * The function must be passed <ul>
22440      * <li>The Record block object</li>
22441      * <li>The "arg" argument from the load function</li>
22442      * <li>A boolean success indicator</li>
22443      * </ul>
22444      * @param {Object} scope The scope in which to call the callback
22445      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22446      */
22447     load : function(params, reader, callback, scope, arg){
22448         if(this.fireEvent("beforeload", this, params) !== false){
22449
22450             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22451
22452             var url = this.url;
22453             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22454             if(this.nocache){
22455                 url += "&_dc=" + (new Date().getTime());
22456             }
22457             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22458             var trans = {
22459                 id : transId,
22460                 cb : "stcCallback"+transId,
22461                 scriptId : "stcScript"+transId,
22462                 params : params,
22463                 arg : arg,
22464                 url : url,
22465                 callback : callback,
22466                 scope : scope,
22467                 reader : reader
22468             };
22469             var conn = this;
22470
22471             window[trans.cb] = function(o){
22472                 conn.handleResponse(o, trans);
22473             };
22474
22475             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22476
22477             if(this.autoAbort !== false){
22478                 this.abort();
22479             }
22480
22481             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22482
22483             var script = document.createElement("script");
22484             script.setAttribute("src", url);
22485             script.setAttribute("type", "text/javascript");
22486             script.setAttribute("id", trans.scriptId);
22487             this.head.appendChild(script);
22488
22489             this.trans = trans;
22490         }else{
22491             callback.call(scope||this, null, arg, false);
22492         }
22493     },
22494
22495     // private
22496     isLoading : function(){
22497         return this.trans ? true : false;
22498     },
22499
22500     /**
22501      * Abort the current server request.
22502      */
22503     abort : function(){
22504         if(this.isLoading()){
22505             this.destroyTrans(this.trans);
22506         }
22507     },
22508
22509     // private
22510     destroyTrans : function(trans, isLoaded){
22511         this.head.removeChild(document.getElementById(trans.scriptId));
22512         clearTimeout(trans.timeoutId);
22513         if(isLoaded){
22514             window[trans.cb] = undefined;
22515             try{
22516                 delete window[trans.cb];
22517             }catch(e){}
22518         }else{
22519             // if hasn't been loaded, wait for load to remove it to prevent script error
22520             window[trans.cb] = function(){
22521                 window[trans.cb] = undefined;
22522                 try{
22523                     delete window[trans.cb];
22524                 }catch(e){}
22525             };
22526         }
22527     },
22528
22529     // private
22530     handleResponse : function(o, trans){
22531         this.trans = false;
22532         this.destroyTrans(trans, true);
22533         var result;
22534         try {
22535             result = trans.reader.readRecords(o);
22536         }catch(e){
22537             this.fireEvent("loadexception", this, o, trans.arg, e);
22538             trans.callback.call(trans.scope||window, null, trans.arg, false);
22539             return;
22540         }
22541         this.fireEvent("load", this, o, trans.arg);
22542         trans.callback.call(trans.scope||window, result, trans.arg, true);
22543     },
22544
22545     // private
22546     handleFailure : function(trans){
22547         this.trans = false;
22548         this.destroyTrans(trans, false);
22549         this.fireEvent("loadexception", this, null, trans.arg);
22550         trans.callback.call(trans.scope||window, null, trans.arg, false);
22551     }
22552 });/*
22553  * Based on:
22554  * Ext JS Library 1.1.1
22555  * Copyright(c) 2006-2007, Ext JS, LLC.
22556  *
22557  * Originally Released Under LGPL - original licence link has changed is not relivant.
22558  *
22559  * Fork - LGPL
22560  * <script type="text/javascript">
22561  */
22562
22563 /**
22564  * @class Roo.data.JsonReader
22565  * @extends Roo.data.DataReader
22566  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22567  * based on mappings in a provided Roo.data.Record constructor.
22568  * 
22569  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22570  * in the reply previously. 
22571  * 
22572  * <p>
22573  * Example code:
22574  * <pre><code>
22575 var RecordDef = Roo.data.Record.create([
22576     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22577     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22578 ]);
22579 var myReader = new Roo.data.JsonReader({
22580     totalProperty: "results",    // The property which contains the total dataset size (optional)
22581     root: "rows",                // The property which contains an Array of row objects
22582     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22583 }, RecordDef);
22584 </code></pre>
22585  * <p>
22586  * This would consume a JSON file like this:
22587  * <pre><code>
22588 { 'results': 2, 'rows': [
22589     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22590     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22591 }
22592 </code></pre>
22593  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22594  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22595  * paged from the remote server.
22596  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22597  * @cfg {String} root name of the property which contains the Array of row objects.
22598  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22599  * @constructor
22600  * Create a new JsonReader
22601  * @param {Object} meta Metadata configuration options
22602  * @param {Object} recordType Either an Array of field definition objects,
22603  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22604  */
22605 Roo.data.JsonReader = function(meta, recordType){
22606     
22607     meta = meta || {};
22608     // set some defaults:
22609     Roo.applyIf(meta, {
22610         totalProperty: 'total',
22611         successProperty : 'success',
22612         root : 'data',
22613         id : 'id'
22614     });
22615     
22616     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22617 };
22618 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22619     
22620     /**
22621      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22622      * Used by Store query builder to append _requestMeta to params.
22623      * 
22624      */
22625     metaFromRemote : false,
22626     /**
22627      * This method is only used by a DataProxy which has retrieved data from a remote server.
22628      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22629      * @return {Object} data A data block which is used by an Roo.data.Store object as
22630      * a cache of Roo.data.Records.
22631      */
22632     read : function(response){
22633         var json = response.responseText;
22634        
22635         var o = /* eval:var:o */ eval("("+json+")");
22636         if(!o) {
22637             throw {message: "JsonReader.read: Json object not found"};
22638         }
22639         
22640         if(o.metaData){
22641             
22642             delete this.ef;
22643             this.metaFromRemote = true;
22644             this.meta = o.metaData;
22645             this.recordType = Roo.data.Record.create(o.metaData.fields);
22646             this.onMetaChange(this.meta, this.recordType, o);
22647         }
22648         return this.readRecords(o);
22649     },
22650
22651     // private function a store will implement
22652     onMetaChange : function(meta, recordType, o){
22653
22654     },
22655
22656     /**
22657          * @ignore
22658          */
22659     simpleAccess: function(obj, subsc) {
22660         return obj[subsc];
22661     },
22662
22663         /**
22664          * @ignore
22665          */
22666     getJsonAccessor: function(){
22667         var re = /[\[\.]/;
22668         return function(expr) {
22669             try {
22670                 return(re.test(expr))
22671                     ? new Function("obj", "return obj." + expr)
22672                     : function(obj){
22673                         return obj[expr];
22674                     };
22675             } catch(e){}
22676             return Roo.emptyFn;
22677         };
22678     }(),
22679
22680     /**
22681      * Create a data block containing Roo.data.Records from an XML document.
22682      * @param {Object} o An object which contains an Array of row objects in the property specified
22683      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22684      * which contains the total size of the dataset.
22685      * @return {Object} data A data block which is used by an Roo.data.Store object as
22686      * a cache of Roo.data.Records.
22687      */
22688     readRecords : function(o){
22689         /**
22690          * After any data loads, the raw JSON data is available for further custom processing.
22691          * @type Object
22692          */
22693         this.o = o;
22694         var s = this.meta, Record = this.recordType,
22695             f = Record.prototype.fields, fi = f.items, fl = f.length;
22696
22697 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22698         if (!this.ef) {
22699             if(s.totalProperty) {
22700                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22701                 }
22702                 if(s.successProperty) {
22703                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22704                 }
22705                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22706                 if (s.id) {
22707                         var g = this.getJsonAccessor(s.id);
22708                         this.getId = function(rec) {
22709                                 var r = g(rec);  
22710                                 return (r === undefined || r === "") ? null : r;
22711                         };
22712                 } else {
22713                         this.getId = function(){return null;};
22714                 }
22715             this.ef = [];
22716             for(var jj = 0; jj < fl; jj++){
22717                 f = fi[jj];
22718                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22719                 this.ef[jj] = this.getJsonAccessor(map);
22720             }
22721         }
22722
22723         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22724         if(s.totalProperty){
22725             var vt = parseInt(this.getTotal(o), 10);
22726             if(!isNaN(vt)){
22727                 totalRecords = vt;
22728             }
22729         }
22730         if(s.successProperty){
22731             var vs = this.getSuccess(o);
22732             if(vs === false || vs === 'false'){
22733                 success = false;
22734             }
22735         }
22736         var records = [];
22737             for(var i = 0; i < c; i++){
22738                     var n = root[i];
22739                 var values = {};
22740                 var id = this.getId(n);
22741                 for(var j = 0; j < fl; j++){
22742                     f = fi[j];
22743                 var v = this.ef[j](n);
22744                 if (!f.convert) {
22745                     Roo.log('missing convert for ' + f.name);
22746                     Roo.log(f);
22747                     continue;
22748                 }
22749                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22750                 }
22751                 var record = new Record(values, id);
22752                 record.json = n;
22753                 records[i] = record;
22754             }
22755             return {
22756             raw : o,
22757                 success : success,
22758                 records : records,
22759                 totalRecords : totalRecords
22760             };
22761     }
22762 });/*
22763  * Based on:
22764  * Ext JS Library 1.1.1
22765  * Copyright(c) 2006-2007, Ext JS, LLC.
22766  *
22767  * Originally Released Under LGPL - original licence link has changed is not relivant.
22768  *
22769  * Fork - LGPL
22770  * <script type="text/javascript">
22771  */
22772
22773 /**
22774  * @class Roo.data.XmlReader
22775  * @extends Roo.data.DataReader
22776  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22777  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22778  * <p>
22779  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22780  * header in the HTTP response must be set to "text/xml".</em>
22781  * <p>
22782  * Example code:
22783  * <pre><code>
22784 var RecordDef = Roo.data.Record.create([
22785    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22786    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22787 ]);
22788 var myReader = new Roo.data.XmlReader({
22789    totalRecords: "results", // The element which contains the total dataset size (optional)
22790    record: "row",           // The repeated element which contains row information
22791    id: "id"                 // The element within the row that provides an ID for the record (optional)
22792 }, RecordDef);
22793 </code></pre>
22794  * <p>
22795  * This would consume an XML file like this:
22796  * <pre><code>
22797 &lt;?xml?>
22798 &lt;dataset>
22799  &lt;results>2&lt;/results>
22800  &lt;row>
22801    &lt;id>1&lt;/id>
22802    &lt;name>Bill&lt;/name>
22803    &lt;occupation>Gardener&lt;/occupation>
22804  &lt;/row>
22805  &lt;row>
22806    &lt;id>2&lt;/id>
22807    &lt;name>Ben&lt;/name>
22808    &lt;occupation>Horticulturalist&lt;/occupation>
22809  &lt;/row>
22810 &lt;/dataset>
22811 </code></pre>
22812  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22813  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22814  * paged from the remote server.
22815  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22816  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22817  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22818  * a record identifier value.
22819  * @constructor
22820  * Create a new XmlReader
22821  * @param {Object} meta Metadata configuration options
22822  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22823  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22824  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22825  */
22826 Roo.data.XmlReader = function(meta, recordType){
22827     meta = meta || {};
22828     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22829 };
22830 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22831     /**
22832      * This method is only used by a DataProxy which has retrieved data from a remote server.
22833          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22834          * to contain a method called 'responseXML' that returns an XML document object.
22835      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22836      * a cache of Roo.data.Records.
22837      */
22838     read : function(response){
22839         var doc = response.responseXML;
22840         if(!doc) {
22841             throw {message: "XmlReader.read: XML Document not available"};
22842         }
22843         return this.readRecords(doc);
22844     },
22845
22846     /**
22847      * Create a data block containing Roo.data.Records from an XML document.
22848          * @param {Object} doc A parsed XML document.
22849      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22850      * a cache of Roo.data.Records.
22851      */
22852     readRecords : function(doc){
22853         /**
22854          * After any data loads/reads, the raw XML Document is available for further custom processing.
22855          * @type XMLDocument
22856          */
22857         this.xmlData = doc;
22858         var root = doc.documentElement || doc;
22859         var q = Roo.DomQuery;
22860         var recordType = this.recordType, fields = recordType.prototype.fields;
22861         var sid = this.meta.id;
22862         var totalRecords = 0, success = true;
22863         if(this.meta.totalRecords){
22864             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22865         }
22866         
22867         if(this.meta.success){
22868             var sv = q.selectValue(this.meta.success, root, true);
22869             success = sv !== false && sv !== 'false';
22870         }
22871         var records = [];
22872         var ns = q.select(this.meta.record, root);
22873         for(var i = 0, len = ns.length; i < len; i++) {
22874                 var n = ns[i];
22875                 var values = {};
22876                 var id = sid ? q.selectValue(sid, n) : undefined;
22877                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22878                     var f = fields.items[j];
22879                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22880                     v = f.convert(v);
22881                     values[f.name] = v;
22882                 }
22883                 var record = new recordType(values, id);
22884                 record.node = n;
22885                 records[records.length] = record;
22886             }
22887
22888             return {
22889                 success : success,
22890                 records : records,
22891                 totalRecords : totalRecords || records.length
22892             };
22893     }
22894 });/*
22895  * Based on:
22896  * Ext JS Library 1.1.1
22897  * Copyright(c) 2006-2007, Ext JS, LLC.
22898  *
22899  * Originally Released Under LGPL - original licence link has changed is not relivant.
22900  *
22901  * Fork - LGPL
22902  * <script type="text/javascript">
22903  */
22904
22905 /**
22906  * @class Roo.data.ArrayReader
22907  * @extends Roo.data.DataReader
22908  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22909  * Each element of that Array represents a row of data fields. The
22910  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22911  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22912  * <p>
22913  * Example code:.
22914  * <pre><code>
22915 var RecordDef = Roo.data.Record.create([
22916     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22917     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22918 ]);
22919 var myReader = new Roo.data.ArrayReader({
22920     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22921 }, RecordDef);
22922 </code></pre>
22923  * <p>
22924  * This would consume an Array like this:
22925  * <pre><code>
22926 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22927   </code></pre>
22928  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22929  * @constructor
22930  * Create a new JsonReader
22931  * @param {Object} meta Metadata configuration options.
22932  * @param {Object} recordType Either an Array of field definition objects
22933  * as specified to {@link Roo.data.Record#create},
22934  * or an {@link Roo.data.Record} object
22935  * created using {@link Roo.data.Record#create}.
22936  */
22937 Roo.data.ArrayReader = function(meta, recordType){
22938     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22939 };
22940
22941 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22942     /**
22943      * Create a data block containing Roo.data.Records from an XML document.
22944      * @param {Object} o An Array of row objects which represents the dataset.
22945      * @return {Object} data A data block which is used by an Roo.data.Store object as
22946      * a cache of Roo.data.Records.
22947      */
22948     readRecords : function(o){
22949         var sid = this.meta ? this.meta.id : null;
22950         var recordType = this.recordType, fields = recordType.prototype.fields;
22951         var records = [];
22952         var root = o;
22953             for(var i = 0; i < root.length; i++){
22954                     var n = root[i];
22955                 var values = {};
22956                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22957                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22958                 var f = fields.items[j];
22959                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22960                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22961                 v = f.convert(v);
22962                 values[f.name] = v;
22963             }
22964                 var record = new recordType(values, id);
22965                 record.json = n;
22966                 records[records.length] = record;
22967             }
22968             return {
22969                 records : records,
22970                 totalRecords : records.length
22971             };
22972     }
22973 });/*
22974  * Based on:
22975  * Ext JS Library 1.1.1
22976  * Copyright(c) 2006-2007, Ext JS, LLC.
22977  *
22978  * Originally Released Under LGPL - original licence link has changed is not relivant.
22979  *
22980  * Fork - LGPL
22981  * <script type="text/javascript">
22982  */
22983
22984
22985 /**
22986  * @class Roo.data.Tree
22987  * @extends Roo.util.Observable
22988  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22989  * in the tree have most standard DOM functionality.
22990  * @constructor
22991  * @param {Node} root (optional) The root node
22992  */
22993 Roo.data.Tree = function(root){
22994    this.nodeHash = {};
22995    /**
22996     * The root node for this tree
22997     * @type Node
22998     */
22999    this.root = null;
23000    if(root){
23001        this.setRootNode(root);
23002    }
23003    this.addEvents({
23004        /**
23005         * @event append
23006         * Fires when a new child node is appended to a node in this tree.
23007         * @param {Tree} tree The owner tree
23008         * @param {Node} parent The parent node
23009         * @param {Node} node The newly appended node
23010         * @param {Number} index The index of the newly appended node
23011         */
23012        "append" : true,
23013        /**
23014         * @event remove
23015         * Fires when a child node is removed from a node in this tree.
23016         * @param {Tree} tree The owner tree
23017         * @param {Node} parent The parent node
23018         * @param {Node} node The child node removed
23019         */
23020        "remove" : true,
23021        /**
23022         * @event move
23023         * Fires when a node is moved to a new location in the tree
23024         * @param {Tree} tree The owner tree
23025         * @param {Node} node The node moved
23026         * @param {Node} oldParent The old parent of this node
23027         * @param {Node} newParent The new parent of this node
23028         * @param {Number} index The index it was moved to
23029         */
23030        "move" : true,
23031        /**
23032         * @event insert
23033         * Fires when a new child node is inserted in a node in this tree.
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} parent The parent node
23036         * @param {Node} node The child node inserted
23037         * @param {Node} refNode The child node the node was inserted before
23038         */
23039        "insert" : true,
23040        /**
23041         * @event beforeappend
23042         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23043         * @param {Tree} tree The owner tree
23044         * @param {Node} parent The parent node
23045         * @param {Node} node The child node to be appended
23046         */
23047        "beforeappend" : true,
23048        /**
23049         * @event beforeremove
23050         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23051         * @param {Tree} tree The owner tree
23052         * @param {Node} parent The parent node
23053         * @param {Node} node The child node to be removed
23054         */
23055        "beforeremove" : true,
23056        /**
23057         * @event beforemove
23058         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23059         * @param {Tree} tree The owner tree
23060         * @param {Node} node The node being moved
23061         * @param {Node} oldParent The parent of the node
23062         * @param {Node} newParent The new parent the node is moving to
23063         * @param {Number} index The index it is being moved to
23064         */
23065        "beforemove" : true,
23066        /**
23067         * @event beforeinsert
23068         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} parent The parent node
23071         * @param {Node} node The child node to be inserted
23072         * @param {Node} refNode The child node the node is being inserted before
23073         */
23074        "beforeinsert" : true
23075    });
23076
23077     Roo.data.Tree.superclass.constructor.call(this);
23078 };
23079
23080 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23081     pathSeparator: "/",
23082
23083     proxyNodeEvent : function(){
23084         return this.fireEvent.apply(this, arguments);
23085     },
23086
23087     /**
23088      * Returns the root node for this tree.
23089      * @return {Node}
23090      */
23091     getRootNode : function(){
23092         return this.root;
23093     },
23094
23095     /**
23096      * Sets the root node for this tree.
23097      * @param {Node} node
23098      * @return {Node}
23099      */
23100     setRootNode : function(node){
23101         this.root = node;
23102         node.ownerTree = this;
23103         node.isRoot = true;
23104         this.registerNode(node);
23105         return node;
23106     },
23107
23108     /**
23109      * Gets a node in this tree by its id.
23110      * @param {String} id
23111      * @return {Node}
23112      */
23113     getNodeById : function(id){
23114         return this.nodeHash[id];
23115     },
23116
23117     registerNode : function(node){
23118         this.nodeHash[node.id] = node;
23119     },
23120
23121     unregisterNode : function(node){
23122         delete this.nodeHash[node.id];
23123     },
23124
23125     toString : function(){
23126         return "[Tree"+(this.id?" "+this.id:"")+"]";
23127     }
23128 });
23129
23130 /**
23131  * @class Roo.data.Node
23132  * @extends Roo.util.Observable
23133  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23134  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23135  * @constructor
23136  * @param {Object} attributes The attributes/config for the node
23137  */
23138 Roo.data.Node = function(attributes){
23139     /**
23140      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23141      * @type {Object}
23142      */
23143     this.attributes = attributes || {};
23144     this.leaf = this.attributes.leaf;
23145     /**
23146      * The node id. @type String
23147      */
23148     this.id = this.attributes.id;
23149     if(!this.id){
23150         this.id = Roo.id(null, "ynode-");
23151         this.attributes.id = this.id;
23152     }
23153      
23154     
23155     /**
23156      * All child nodes of this node. @type Array
23157      */
23158     this.childNodes = [];
23159     if(!this.childNodes.indexOf){ // indexOf is a must
23160         this.childNodes.indexOf = function(o){
23161             for(var i = 0, len = this.length; i < len; i++){
23162                 if(this[i] == o) {
23163                     return i;
23164                 }
23165             }
23166             return -1;
23167         };
23168     }
23169     /**
23170      * The parent node for this node. @type Node
23171      */
23172     this.parentNode = null;
23173     /**
23174      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23175      */
23176     this.firstChild = null;
23177     /**
23178      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23179      */
23180     this.lastChild = null;
23181     /**
23182      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23183      */
23184     this.previousSibling = null;
23185     /**
23186      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23187      */
23188     this.nextSibling = null;
23189
23190     this.addEvents({
23191        /**
23192         * @event append
23193         * Fires when a new child node is appended
23194         * @param {Tree} tree The owner tree
23195         * @param {Node} this This node
23196         * @param {Node} node The newly appended node
23197         * @param {Number} index The index of the newly appended node
23198         */
23199        "append" : true,
23200        /**
23201         * @event remove
23202         * Fires when a child node is removed
23203         * @param {Tree} tree The owner tree
23204         * @param {Node} this This node
23205         * @param {Node} node The removed node
23206         */
23207        "remove" : true,
23208        /**
23209         * @event move
23210         * Fires when this node is moved to a new location in the tree
23211         * @param {Tree} tree The owner tree
23212         * @param {Node} this This node
23213         * @param {Node} oldParent The old parent of this node
23214         * @param {Node} newParent The new parent of this node
23215         * @param {Number} index The index it was moved to
23216         */
23217        "move" : true,
23218        /**
23219         * @event insert
23220         * Fires when a new child node is inserted.
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} node The child node inserted
23224         * @param {Node} refNode The child node the node was inserted before
23225         */
23226        "insert" : true,
23227        /**
23228         * @event beforeappend
23229         * Fires before a new child is appended, return false to cancel the append.
23230         * @param {Tree} tree The owner tree
23231         * @param {Node} this This node
23232         * @param {Node} node The child node to be appended
23233         */
23234        "beforeappend" : true,
23235        /**
23236         * @event beforeremove
23237         * Fires before a child is removed, return false to cancel the remove.
23238         * @param {Tree} tree The owner tree
23239         * @param {Node} this This node
23240         * @param {Node} node The child node to be removed
23241         */
23242        "beforeremove" : true,
23243        /**
23244         * @event beforemove
23245         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23246         * @param {Tree} tree The owner tree
23247         * @param {Node} this This node
23248         * @param {Node} oldParent The parent of this node
23249         * @param {Node} newParent The new parent this node is moving to
23250         * @param {Number} index The index it is being moved to
23251         */
23252        "beforemove" : true,
23253        /**
23254         * @event beforeinsert
23255         * Fires before a new child is inserted, return false to cancel the insert.
23256         * @param {Tree} tree The owner tree
23257         * @param {Node} this This node
23258         * @param {Node} node The child node to be inserted
23259         * @param {Node} refNode The child node the node is being inserted before
23260         */
23261        "beforeinsert" : true
23262    });
23263     this.listeners = this.attributes.listeners;
23264     Roo.data.Node.superclass.constructor.call(this);
23265 };
23266
23267 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23268     fireEvent : function(evtName){
23269         // first do standard event for this node
23270         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23271             return false;
23272         }
23273         // then bubble it up to the tree if the event wasn't cancelled
23274         var ot = this.getOwnerTree();
23275         if(ot){
23276             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23277                 return false;
23278             }
23279         }
23280         return true;
23281     },
23282
23283     /**
23284      * Returns true if this node is a leaf
23285      * @return {Boolean}
23286      */
23287     isLeaf : function(){
23288         return this.leaf === true;
23289     },
23290
23291     // private
23292     setFirstChild : function(node){
23293         this.firstChild = node;
23294     },
23295
23296     //private
23297     setLastChild : function(node){
23298         this.lastChild = node;
23299     },
23300
23301
23302     /**
23303      * Returns true if this node is the last child of its parent
23304      * @return {Boolean}
23305      */
23306     isLast : function(){
23307        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23308     },
23309
23310     /**
23311      * Returns true if this node is the first child of its parent
23312      * @return {Boolean}
23313      */
23314     isFirst : function(){
23315        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23316     },
23317
23318     hasChildNodes : function(){
23319         return !this.isLeaf() && this.childNodes.length > 0;
23320     },
23321
23322     /**
23323      * Insert node(s) as the last child node of this node.
23324      * @param {Node/Array} node The node or Array of nodes to append
23325      * @return {Node} The appended node if single append, or null if an array was passed
23326      */
23327     appendChild : function(node){
23328         var multi = false;
23329         if(node instanceof Array){
23330             multi = node;
23331         }else if(arguments.length > 1){
23332             multi = arguments;
23333         }
23334         // if passed an array or multiple args do them one by one
23335         if(multi){
23336             for(var i = 0, len = multi.length; i < len; i++) {
23337                 this.appendChild(multi[i]);
23338             }
23339         }else{
23340             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23341                 return false;
23342             }
23343             var index = this.childNodes.length;
23344             var oldParent = node.parentNode;
23345             // it's a move, make sure we move it cleanly
23346             if(oldParent){
23347                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23348                     return false;
23349                 }
23350                 oldParent.removeChild(node);
23351             }
23352             index = this.childNodes.length;
23353             if(index == 0){
23354                 this.setFirstChild(node);
23355             }
23356             this.childNodes.push(node);
23357             node.parentNode = this;
23358             var ps = this.childNodes[index-1];
23359             if(ps){
23360                 node.previousSibling = ps;
23361                 ps.nextSibling = node;
23362             }else{
23363                 node.previousSibling = null;
23364             }
23365             node.nextSibling = null;
23366             this.setLastChild(node);
23367             node.setOwnerTree(this.getOwnerTree());
23368             this.fireEvent("append", this.ownerTree, this, node, index);
23369             if(oldParent){
23370                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23371             }
23372             return node;
23373         }
23374     },
23375
23376     /**
23377      * Removes a child node from this node.
23378      * @param {Node} node The node to remove
23379      * @return {Node} The removed node
23380      */
23381     removeChild : function(node){
23382         var index = this.childNodes.indexOf(node);
23383         if(index == -1){
23384             return false;
23385         }
23386         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23387             return false;
23388         }
23389
23390         // remove it from childNodes collection
23391         this.childNodes.splice(index, 1);
23392
23393         // update siblings
23394         if(node.previousSibling){
23395             node.previousSibling.nextSibling = node.nextSibling;
23396         }
23397         if(node.nextSibling){
23398             node.nextSibling.previousSibling = node.previousSibling;
23399         }
23400
23401         // update child refs
23402         if(this.firstChild == node){
23403             this.setFirstChild(node.nextSibling);
23404         }
23405         if(this.lastChild == node){
23406             this.setLastChild(node.previousSibling);
23407         }
23408
23409         node.setOwnerTree(null);
23410         // clear any references from the node
23411         node.parentNode = null;
23412         node.previousSibling = null;
23413         node.nextSibling = null;
23414         this.fireEvent("remove", this.ownerTree, this, node);
23415         return node;
23416     },
23417
23418     /**
23419      * Inserts the first node before the second node in this nodes childNodes collection.
23420      * @param {Node} node The node to insert
23421      * @param {Node} refNode The node to insert before (if null the node is appended)
23422      * @return {Node} The inserted node
23423      */
23424     insertBefore : function(node, refNode){
23425         if(!refNode){ // like standard Dom, refNode can be null for append
23426             return this.appendChild(node);
23427         }
23428         // nothing to do
23429         if(node == refNode){
23430             return false;
23431         }
23432
23433         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23434             return false;
23435         }
23436         var index = this.childNodes.indexOf(refNode);
23437         var oldParent = node.parentNode;
23438         var refIndex = index;
23439
23440         // when moving internally, indexes will change after remove
23441         if(oldParent == this && this.childNodes.indexOf(node) < index){
23442             refIndex--;
23443         }
23444
23445         // it's a move, make sure we move it cleanly
23446         if(oldParent){
23447             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23448                 return false;
23449             }
23450             oldParent.removeChild(node);
23451         }
23452         if(refIndex == 0){
23453             this.setFirstChild(node);
23454         }
23455         this.childNodes.splice(refIndex, 0, node);
23456         node.parentNode = this;
23457         var ps = this.childNodes[refIndex-1];
23458         if(ps){
23459             node.previousSibling = ps;
23460             ps.nextSibling = node;
23461         }else{
23462             node.previousSibling = null;
23463         }
23464         node.nextSibling = refNode;
23465         refNode.previousSibling = node;
23466         node.setOwnerTree(this.getOwnerTree());
23467         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23468         if(oldParent){
23469             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23470         }
23471         return node;
23472     },
23473
23474     /**
23475      * Returns the child node at the specified index.
23476      * @param {Number} index
23477      * @return {Node}
23478      */
23479     item : function(index){
23480         return this.childNodes[index];
23481     },
23482
23483     /**
23484      * Replaces one child node in this node with another.
23485      * @param {Node} newChild The replacement node
23486      * @param {Node} oldChild The node to replace
23487      * @return {Node} The replaced node
23488      */
23489     replaceChild : function(newChild, oldChild){
23490         this.insertBefore(newChild, oldChild);
23491         this.removeChild(oldChild);
23492         return oldChild;
23493     },
23494
23495     /**
23496      * Returns the index of a child node
23497      * @param {Node} node
23498      * @return {Number} The index of the node or -1 if it was not found
23499      */
23500     indexOf : function(child){
23501         return this.childNodes.indexOf(child);
23502     },
23503
23504     /**
23505      * Returns the tree this node is in.
23506      * @return {Tree}
23507      */
23508     getOwnerTree : function(){
23509         // if it doesn't have one, look for one
23510         if(!this.ownerTree){
23511             var p = this;
23512             while(p){
23513                 if(p.ownerTree){
23514                     this.ownerTree = p.ownerTree;
23515                     break;
23516                 }
23517                 p = p.parentNode;
23518             }
23519         }
23520         return this.ownerTree;
23521     },
23522
23523     /**
23524      * Returns depth of this node (the root node has a depth of 0)
23525      * @return {Number}
23526      */
23527     getDepth : function(){
23528         var depth = 0;
23529         var p = this;
23530         while(p.parentNode){
23531             ++depth;
23532             p = p.parentNode;
23533         }
23534         return depth;
23535     },
23536
23537     // private
23538     setOwnerTree : function(tree){
23539         // if it's move, we need to update everyone
23540         if(tree != this.ownerTree){
23541             if(this.ownerTree){
23542                 this.ownerTree.unregisterNode(this);
23543             }
23544             this.ownerTree = tree;
23545             var cs = this.childNodes;
23546             for(var i = 0, len = cs.length; i < len; i++) {
23547                 cs[i].setOwnerTree(tree);
23548             }
23549             if(tree){
23550                 tree.registerNode(this);
23551             }
23552         }
23553     },
23554
23555     /**
23556      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23557      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23558      * @return {String} The path
23559      */
23560     getPath : function(attr){
23561         attr = attr || "id";
23562         var p = this.parentNode;
23563         var b = [this.attributes[attr]];
23564         while(p){
23565             b.unshift(p.attributes[attr]);
23566             p = p.parentNode;
23567         }
23568         var sep = this.getOwnerTree().pathSeparator;
23569         return sep + b.join(sep);
23570     },
23571
23572     /**
23573      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23574      * function call will be the scope provided or the current node. The arguments to the function
23575      * will be the args provided or the current node. If the function returns false at any point,
23576      * the bubble is stopped.
23577      * @param {Function} fn The function to call
23578      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23579      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23580      */
23581     bubble : function(fn, scope, args){
23582         var p = this;
23583         while(p){
23584             if(fn.call(scope || p, args || p) === false){
23585                 break;
23586             }
23587             p = p.parentNode;
23588         }
23589     },
23590
23591     /**
23592      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23593      * function call will be the scope provided or the current node. The arguments to the function
23594      * will be the args provided or the current node. If the function returns false at any point,
23595      * the cascade is stopped on that branch.
23596      * @param {Function} fn The function to call
23597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23599      */
23600     cascade : function(fn, scope, args){
23601         if(fn.call(scope || this, args || this) !== false){
23602             var cs = this.childNodes;
23603             for(var i = 0, len = cs.length; i < len; i++) {
23604                 cs[i].cascade(fn, scope, args);
23605             }
23606         }
23607     },
23608
23609     /**
23610      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23611      * function call will be the scope provided or the current node. The arguments to the function
23612      * will be the args provided or the current node. If the function returns false at any point,
23613      * the iteration stops.
23614      * @param {Function} fn The function to call
23615      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23616      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23617      */
23618     eachChild : function(fn, scope, args){
23619         var cs = this.childNodes;
23620         for(var i = 0, len = cs.length; i < len; i++) {
23621                 if(fn.call(scope || this, args || cs[i]) === false){
23622                     break;
23623                 }
23624         }
23625     },
23626
23627     /**
23628      * Finds the first child that has the attribute with the specified value.
23629      * @param {String} attribute The attribute name
23630      * @param {Mixed} value The value to search for
23631      * @return {Node} The found child or null if none was found
23632      */
23633     findChild : function(attribute, value){
23634         var cs = this.childNodes;
23635         for(var i = 0, len = cs.length; i < len; i++) {
23636                 if(cs[i].attributes[attribute] == value){
23637                     return cs[i];
23638                 }
23639         }
23640         return null;
23641     },
23642
23643     /**
23644      * Finds the first child by a custom function. The child matches if the function passed
23645      * returns true.
23646      * @param {Function} fn
23647      * @param {Object} scope (optional)
23648      * @return {Node} The found child or null if none was found
23649      */
23650     findChildBy : function(fn, scope){
23651         var cs = this.childNodes;
23652         for(var i = 0, len = cs.length; i < len; i++) {
23653                 if(fn.call(scope||cs[i], cs[i]) === true){
23654                     return cs[i];
23655                 }
23656         }
23657         return null;
23658     },
23659
23660     /**
23661      * Sorts this nodes children using the supplied sort function
23662      * @param {Function} fn
23663      * @param {Object} scope (optional)
23664      */
23665     sort : function(fn, scope){
23666         var cs = this.childNodes;
23667         var len = cs.length;
23668         if(len > 0){
23669             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23670             cs.sort(sortFn);
23671             for(var i = 0; i < len; i++){
23672                 var n = cs[i];
23673                 n.previousSibling = cs[i-1];
23674                 n.nextSibling = cs[i+1];
23675                 if(i == 0){
23676                     this.setFirstChild(n);
23677                 }
23678                 if(i == len-1){
23679                     this.setLastChild(n);
23680                 }
23681             }
23682         }
23683     },
23684
23685     /**
23686      * Returns true if this node is an ancestor (at any point) of the passed node.
23687      * @param {Node} node
23688      * @return {Boolean}
23689      */
23690     contains : function(node){
23691         return node.isAncestor(this);
23692     },
23693
23694     /**
23695      * Returns true if the passed node is an ancestor (at any point) of this node.
23696      * @param {Node} node
23697      * @return {Boolean}
23698      */
23699     isAncestor : function(node){
23700         var p = this.parentNode;
23701         while(p){
23702             if(p == node){
23703                 return true;
23704             }
23705             p = p.parentNode;
23706         }
23707         return false;
23708     },
23709
23710     toString : function(){
23711         return "[Node"+(this.id?" "+this.id:"")+"]";
23712     }
23713 });/*
23714  * Based on:
23715  * Ext JS Library 1.1.1
23716  * Copyright(c) 2006-2007, Ext JS, LLC.
23717  *
23718  * Originally Released Under LGPL - original licence link has changed is not relivant.
23719  *
23720  * Fork - LGPL
23721  * <script type="text/javascript">
23722  */
23723  (function(){ 
23724 /**
23725  * @class Roo.Layer
23726  * @extends Roo.Element
23727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23728  * automatic maintaining of shadow/shim positions.
23729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23731  * you can pass a string with a CSS class name. False turns off the shadow.
23732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23734  * @cfg {String} cls CSS class to add to the element
23735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23737  * @constructor
23738  * @param {Object} config An object with config options.
23739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23740  */
23741
23742 Roo.Layer = function(config, existingEl){
23743     config = config || {};
23744     var dh = Roo.DomHelper;
23745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23746     if(existingEl){
23747         this.dom = Roo.getDom(existingEl);
23748     }
23749     if(!this.dom){
23750         var o = config.dh || {tag: "div", cls: "x-layer"};
23751         this.dom = dh.append(pel, o);
23752     }
23753     if(config.cls){
23754         this.addClass(config.cls);
23755     }
23756     this.constrain = config.constrain !== false;
23757     this.visibilityMode = Roo.Element.VISIBILITY;
23758     if(config.id){
23759         this.id = this.dom.id = config.id;
23760     }else{
23761         this.id = Roo.id(this.dom);
23762     }
23763     this.zindex = config.zindex || this.getZIndex();
23764     this.position("absolute", this.zindex);
23765     if(config.shadow){
23766         this.shadowOffset = config.shadowOffset || 4;
23767         this.shadow = new Roo.Shadow({
23768             offset : this.shadowOffset,
23769             mode : config.shadow
23770         });
23771     }else{
23772         this.shadowOffset = 0;
23773     }
23774     this.useShim = config.shim !== false && Roo.useShims;
23775     this.useDisplay = config.useDisplay;
23776     this.hide();
23777 };
23778
23779 var supr = Roo.Element.prototype;
23780
23781 // shims are shared among layer to keep from having 100 iframes
23782 var shims = [];
23783
23784 Roo.extend(Roo.Layer, Roo.Element, {
23785
23786     getZIndex : function(){
23787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23788     },
23789
23790     getShim : function(){
23791         if(!this.useShim){
23792             return null;
23793         }
23794         if(this.shim){
23795             return this.shim;
23796         }
23797         var shim = shims.shift();
23798         if(!shim){
23799             shim = this.createShim();
23800             shim.enableDisplayMode('block');
23801             shim.dom.style.display = 'none';
23802             shim.dom.style.visibility = 'visible';
23803         }
23804         var pn = this.dom.parentNode;
23805         if(shim.dom.parentNode != pn){
23806             pn.insertBefore(shim.dom, this.dom);
23807         }
23808         shim.setStyle('z-index', this.getZIndex()-2);
23809         this.shim = shim;
23810         return shim;
23811     },
23812
23813     hideShim : function(){
23814         if(this.shim){
23815             this.shim.setDisplayed(false);
23816             shims.push(this.shim);
23817             delete this.shim;
23818         }
23819     },
23820
23821     disableShadow : function(){
23822         if(this.shadow){
23823             this.shadowDisabled = true;
23824             this.shadow.hide();
23825             this.lastShadowOffset = this.shadowOffset;
23826             this.shadowOffset = 0;
23827         }
23828     },
23829
23830     enableShadow : function(show){
23831         if(this.shadow){
23832             this.shadowDisabled = false;
23833             this.shadowOffset = this.lastShadowOffset;
23834             delete this.lastShadowOffset;
23835             if(show){
23836                 this.sync(true);
23837             }
23838         }
23839     },
23840
23841     // private
23842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23844     sync : function(doShow){
23845         var sw = this.shadow;
23846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23847             var sh = this.getShim();
23848
23849             var w = this.getWidth(),
23850                 h = this.getHeight();
23851
23852             var l = this.getLeft(true),
23853                 t = this.getTop(true);
23854
23855             if(sw && !this.shadowDisabled){
23856                 if(doShow && !sw.isVisible()){
23857                     sw.show(this);
23858                 }else{
23859                     sw.realign(l, t, w, h);
23860                 }
23861                 if(sh){
23862                     if(doShow){
23863                        sh.show();
23864                     }
23865                     // fit the shim behind the shadow, so it is shimmed too
23866                     var a = sw.adjusts, s = sh.dom.style;
23867                     s.left = (Math.min(l, l+a.l))+"px";
23868                     s.top = (Math.min(t, t+a.t))+"px";
23869                     s.width = (w+a.w)+"px";
23870                     s.height = (h+a.h)+"px";
23871                 }
23872             }else if(sh){
23873                 if(doShow){
23874                    sh.show();
23875                 }
23876                 sh.setSize(w, h);
23877                 sh.setLeftTop(l, t);
23878             }
23879             
23880         }
23881     },
23882
23883     // private
23884     destroy : function(){
23885         this.hideShim();
23886         if(this.shadow){
23887             this.shadow.hide();
23888         }
23889         this.removeAllListeners();
23890         var pn = this.dom.parentNode;
23891         if(pn){
23892             pn.removeChild(this.dom);
23893         }
23894         Roo.Element.uncache(this.id);
23895     },
23896
23897     remove : function(){
23898         this.destroy();
23899     },
23900
23901     // private
23902     beginUpdate : function(){
23903         this.updating = true;
23904     },
23905
23906     // private
23907     endUpdate : function(){
23908         this.updating = false;
23909         this.sync(true);
23910     },
23911
23912     // private
23913     hideUnders : function(negOffset){
23914         if(this.shadow){
23915             this.shadow.hide();
23916         }
23917         this.hideShim();
23918     },
23919
23920     // private
23921     constrainXY : function(){
23922         if(this.constrain){
23923             var vw = Roo.lib.Dom.getViewWidth(),
23924                 vh = Roo.lib.Dom.getViewHeight();
23925             var s = Roo.get(document).getScroll();
23926
23927             var xy = this.getXY();
23928             var x = xy[0], y = xy[1];   
23929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23930             // only move it if it needs it
23931             var moved = false;
23932             // first validate right/bottom
23933             if((x + w) > vw+s.left){
23934                 x = vw - w - this.shadowOffset;
23935                 moved = true;
23936             }
23937             if((y + h) > vh+s.top){
23938                 y = vh - h - this.shadowOffset;
23939                 moved = true;
23940             }
23941             // then make sure top/left isn't negative
23942             if(x < s.left){
23943                 x = s.left;
23944                 moved = true;
23945             }
23946             if(y < s.top){
23947                 y = s.top;
23948                 moved = true;
23949             }
23950             if(moved){
23951                 if(this.avoidY){
23952                     var ay = this.avoidY;
23953                     if(y <= ay && (y+h) >= ay){
23954                         y = ay-h-5;   
23955                     }
23956                 }
23957                 xy = [x, y];
23958                 this.storeXY(xy);
23959                 supr.setXY.call(this, xy);
23960                 this.sync();
23961             }
23962         }
23963     },
23964
23965     isVisible : function(){
23966         return this.visible;    
23967     },
23968
23969     // private
23970     showAction : function(){
23971         this.visible = true; // track visibility to prevent getStyle calls
23972         if(this.useDisplay === true){
23973             this.setDisplayed("");
23974         }else if(this.lastXY){
23975             supr.setXY.call(this, this.lastXY);
23976         }else if(this.lastLT){
23977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23978         }
23979     },
23980
23981     // private
23982     hideAction : function(){
23983         this.visible = false;
23984         if(this.useDisplay === true){
23985             this.setDisplayed(false);
23986         }else{
23987             this.setLeftTop(-10000,-10000);
23988         }
23989     },
23990
23991     // overridden Element method
23992     setVisible : function(v, a, d, c, e){
23993         if(v){
23994             this.showAction();
23995         }
23996         if(a && v){
23997             var cb = function(){
23998                 this.sync(true);
23999                 if(c){
24000                     c();
24001                 }
24002             }.createDelegate(this);
24003             supr.setVisible.call(this, true, true, d, cb, e);
24004         }else{
24005             if(!v){
24006                 this.hideUnders(true);
24007             }
24008             var cb = c;
24009             if(a){
24010                 cb = function(){
24011                     this.hideAction();
24012                     if(c){
24013                         c();
24014                     }
24015                 }.createDelegate(this);
24016             }
24017             supr.setVisible.call(this, v, a, d, cb, e);
24018             if(v){
24019                 this.sync(true);
24020             }else if(!a){
24021                 this.hideAction();
24022             }
24023         }
24024     },
24025
24026     storeXY : function(xy){
24027         delete this.lastLT;
24028         this.lastXY = xy;
24029     },
24030
24031     storeLeftTop : function(left, top){
24032         delete this.lastXY;
24033         this.lastLT = [left, top];
24034     },
24035
24036     // private
24037     beforeFx : function(){
24038         this.beforeAction();
24039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24040     },
24041
24042     // private
24043     afterFx : function(){
24044         Roo.Layer.superclass.afterFx.apply(this, arguments);
24045         this.sync(this.isVisible());
24046     },
24047
24048     // private
24049     beforeAction : function(){
24050         if(!this.updating && this.shadow){
24051             this.shadow.hide();
24052         }
24053     },
24054
24055     // overridden Element method
24056     setLeft : function(left){
24057         this.storeLeftTop(left, this.getTop(true));
24058         supr.setLeft.apply(this, arguments);
24059         this.sync();
24060     },
24061
24062     setTop : function(top){
24063         this.storeLeftTop(this.getLeft(true), top);
24064         supr.setTop.apply(this, arguments);
24065         this.sync();
24066     },
24067
24068     setLeftTop : function(left, top){
24069         this.storeLeftTop(left, top);
24070         supr.setLeftTop.apply(this, arguments);
24071         this.sync();
24072     },
24073
24074     setXY : function(xy, a, d, c, e){
24075         this.fixDisplay();
24076         this.beforeAction();
24077         this.storeXY(xy);
24078         var cb = this.createCB(c);
24079         supr.setXY.call(this, xy, a, d, cb, e);
24080         if(!a){
24081             cb();
24082         }
24083     },
24084
24085     // private
24086     createCB : function(c){
24087         var el = this;
24088         return function(){
24089             el.constrainXY();
24090             el.sync(true);
24091             if(c){
24092                 c();
24093             }
24094         };
24095     },
24096
24097     // overridden Element method
24098     setX : function(x, a, d, c, e){
24099         this.setXY([x, this.getY()], a, d, c, e);
24100     },
24101
24102     // overridden Element method
24103     setY : function(y, a, d, c, e){
24104         this.setXY([this.getX(), y], a, d, c, e);
24105     },
24106
24107     // overridden Element method
24108     setSize : function(w, h, a, d, c, e){
24109         this.beforeAction();
24110         var cb = this.createCB(c);
24111         supr.setSize.call(this, w, h, a, d, cb, e);
24112         if(!a){
24113             cb();
24114         }
24115     },
24116
24117     // overridden Element method
24118     setWidth : function(w, a, d, c, e){
24119         this.beforeAction();
24120         var cb = this.createCB(c);
24121         supr.setWidth.call(this, w, a, d, cb, e);
24122         if(!a){
24123             cb();
24124         }
24125     },
24126
24127     // overridden Element method
24128     setHeight : function(h, a, d, c, e){
24129         this.beforeAction();
24130         var cb = this.createCB(c);
24131         supr.setHeight.call(this, h, a, d, cb, e);
24132         if(!a){
24133             cb();
24134         }
24135     },
24136
24137     // overridden Element method
24138     setBounds : function(x, y, w, h, a, d, c, e){
24139         this.beforeAction();
24140         var cb = this.createCB(c);
24141         if(!a){
24142             this.storeXY([x, y]);
24143             supr.setXY.call(this, [x, y]);
24144             supr.setSize.call(this, w, h, a, d, cb, e);
24145             cb();
24146         }else{
24147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24148         }
24149         return this;
24150     },
24151     
24152     /**
24153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24156      * @param {Number} zindex The new z-index to set
24157      * @return {this} The Layer
24158      */
24159     setZIndex : function(zindex){
24160         this.zindex = zindex;
24161         this.setStyle("z-index", zindex + 2);
24162         if(this.shadow){
24163             this.shadow.setZIndex(zindex + 1);
24164         }
24165         if(this.shim){
24166             this.shim.setStyle("z-index", zindex);
24167         }
24168     }
24169 });
24170 })();/*
24171  * Based on:
24172  * Ext JS Library 1.1.1
24173  * Copyright(c) 2006-2007, Ext JS, LLC.
24174  *
24175  * Originally Released Under LGPL - original licence link has changed is not relivant.
24176  *
24177  * Fork - LGPL
24178  * <script type="text/javascript">
24179  */
24180
24181
24182 /**
24183  * @class Roo.Shadow
24184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24187  * @constructor
24188  * Create a new Shadow
24189  * @param {Object} config The config object
24190  */
24191 Roo.Shadow = function(config){
24192     Roo.apply(this, config);
24193     if(typeof this.mode != "string"){
24194         this.mode = this.defaultMode;
24195     }
24196     var o = this.offset, a = {h: 0};
24197     var rad = Math.floor(this.offset/2);
24198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24199         case "drop":
24200             a.w = 0;
24201             a.l = a.t = o;
24202             a.t -= 1;
24203             if(Roo.isIE){
24204                 a.l -= this.offset + rad;
24205                 a.t -= this.offset + rad;
24206                 a.w -= rad;
24207                 a.h -= rad;
24208                 a.t += 1;
24209             }
24210         break;
24211         case "sides":
24212             a.w = (o*2);
24213             a.l = -o;
24214             a.t = o-1;
24215             if(Roo.isIE){
24216                 a.l -= (this.offset - rad);
24217                 a.t -= this.offset + rad;
24218                 a.l += 1;
24219                 a.w -= (this.offset - rad)*2;
24220                 a.w -= rad + 1;
24221                 a.h -= 1;
24222             }
24223         break;
24224         case "frame":
24225             a.w = a.h = (o*2);
24226             a.l = a.t = -o;
24227             a.t += 1;
24228             a.h -= 2;
24229             if(Roo.isIE){
24230                 a.l -= (this.offset - rad);
24231                 a.t -= (this.offset - rad);
24232                 a.l += 1;
24233                 a.w -= (this.offset + rad + 1);
24234                 a.h -= (this.offset + rad);
24235                 a.h += 1;
24236             }
24237         break;
24238     };
24239
24240     this.adjusts = a;
24241 };
24242
24243 Roo.Shadow.prototype = {
24244     /**
24245      * @cfg {String} mode
24246      * The shadow display mode.  Supports the following options:<br />
24247      * sides: Shadow displays on both sides and bottom only<br />
24248      * frame: Shadow displays equally on all four sides<br />
24249      * drop: Traditional bottom-right drop shadow (default)
24250      */
24251     /**
24252      * @cfg {String} offset
24253      * The number of pixels to offset the shadow from the element (defaults to 4)
24254      */
24255     offset: 4,
24256
24257     // private
24258     defaultMode: "drop",
24259
24260     /**
24261      * Displays the shadow under the target element
24262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24263      */
24264     show : function(target){
24265         target = Roo.get(target);
24266         if(!this.el){
24267             this.el = Roo.Shadow.Pool.pull();
24268             if(this.el.dom.nextSibling != target.dom){
24269                 this.el.insertBefore(target);
24270             }
24271         }
24272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24273         if(Roo.isIE){
24274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24275         }
24276         this.realign(
24277             target.getLeft(true),
24278             target.getTop(true),
24279             target.getWidth(),
24280             target.getHeight()
24281         );
24282         this.el.dom.style.display = "block";
24283     },
24284
24285     /**
24286      * Returns true if the shadow is visible, else false
24287      */
24288     isVisible : function(){
24289         return this.el ? true : false;  
24290     },
24291
24292     /**
24293      * Direct alignment when values are already available. Show must be called at least once before
24294      * calling this method to ensure it is initialized.
24295      * @param {Number} left The target element left position
24296      * @param {Number} top The target element top position
24297      * @param {Number} width The target element width
24298      * @param {Number} height The target element height
24299      */
24300     realign : function(l, t, w, h){
24301         if(!this.el){
24302             return;
24303         }
24304         var a = this.adjusts, d = this.el.dom, s = d.style;
24305         var iea = 0;
24306         s.left = (l+a.l)+"px";
24307         s.top = (t+a.t)+"px";
24308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24309  
24310         if(s.width != sws || s.height != shs){
24311             s.width = sws;
24312             s.height = shs;
24313             if(!Roo.isIE){
24314                 var cn = d.childNodes;
24315                 var sww = Math.max(0, (sw-12))+"px";
24316                 cn[0].childNodes[1].style.width = sww;
24317                 cn[1].childNodes[1].style.width = sww;
24318                 cn[2].childNodes[1].style.width = sww;
24319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24320             }
24321         }
24322     },
24323
24324     /**
24325      * Hides this shadow
24326      */
24327     hide : function(){
24328         if(this.el){
24329             this.el.dom.style.display = "none";
24330             Roo.Shadow.Pool.push(this.el);
24331             delete this.el;
24332         }
24333     },
24334
24335     /**
24336      * Adjust the z-index of this shadow
24337      * @param {Number} zindex The new z-index
24338      */
24339     setZIndex : function(z){
24340         this.zIndex = z;
24341         if(this.el){
24342             this.el.setStyle("z-index", z);
24343         }
24344     }
24345 };
24346
24347 // Private utility class that manages the internal Shadow cache
24348 Roo.Shadow.Pool = function(){
24349     var p = [];
24350     var markup = Roo.isIE ?
24351                  '<div class="x-ie-shadow"></div>' :
24352                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24353     return {
24354         pull : function(){
24355             var sh = p.shift();
24356             if(!sh){
24357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24358                 sh.autoBoxAdjust = false;
24359             }
24360             return sh;
24361         },
24362
24363         push : function(sh){
24364             p.push(sh);
24365         }
24366     };
24367 }();/*
24368  * Based on:
24369  * Ext JS Library 1.1.1
24370  * Copyright(c) 2006-2007, Ext JS, LLC.
24371  *
24372  * Originally Released Under LGPL - original licence link has changed is not relivant.
24373  *
24374  * Fork - LGPL
24375  * <script type="text/javascript">
24376  */
24377
24378
24379 /**
24380  * @class Roo.SplitBar
24381  * @extends Roo.util.Observable
24382  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24383  * <br><br>
24384  * Usage:
24385  * <pre><code>
24386 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24387                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24388 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24389 split.minSize = 100;
24390 split.maxSize = 600;
24391 split.animate = true;
24392 split.on('moved', splitterMoved);
24393 </code></pre>
24394  * @constructor
24395  * Create a new SplitBar
24396  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24397  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24398  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24399  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24400                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24401                         position of the SplitBar).
24402  */
24403 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24404     
24405     /** @private */
24406     this.el = Roo.get(dragElement, true);
24407     this.el.dom.unselectable = "on";
24408     /** @private */
24409     this.resizingEl = Roo.get(resizingElement, true);
24410
24411     /**
24412      * @private
24413      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24414      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24415      * @type Number
24416      */
24417     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24418     
24419     /**
24420      * The minimum size of the resizing element. (Defaults to 0)
24421      * @type Number
24422      */
24423     this.minSize = 0;
24424     
24425     /**
24426      * The maximum size of the resizing element. (Defaults to 2000)
24427      * @type Number
24428      */
24429     this.maxSize = 2000;
24430     
24431     /**
24432      * Whether to animate the transition to the new size
24433      * @type Boolean
24434      */
24435     this.animate = false;
24436     
24437     /**
24438      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24439      * @type Boolean
24440      */
24441     this.useShim = false;
24442     
24443     /** @private */
24444     this.shim = null;
24445     
24446     if(!existingProxy){
24447         /** @private */
24448         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24449     }else{
24450         this.proxy = Roo.get(existingProxy).dom;
24451     }
24452     /** @private */
24453     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24454     
24455     /** @private */
24456     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24457     
24458     /** @private */
24459     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24460     
24461     /** @private */
24462     this.dragSpecs = {};
24463     
24464     /**
24465      * @private The adapter to use to positon and resize elements
24466      */
24467     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24468     this.adapter.init(this);
24469     
24470     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24471         /** @private */
24472         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24473         this.el.addClass("x-splitbar-h");
24474     }else{
24475         /** @private */
24476         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24477         this.el.addClass("x-splitbar-v");
24478     }
24479     
24480     this.addEvents({
24481         /**
24482          * @event resize
24483          * Fires when the splitter is moved (alias for {@link #event-moved})
24484          * @param {Roo.SplitBar} this
24485          * @param {Number} newSize the new width or height
24486          */
24487         "resize" : true,
24488         /**
24489          * @event moved
24490          * Fires when the splitter is moved
24491          * @param {Roo.SplitBar} this
24492          * @param {Number} newSize the new width or height
24493          */
24494         "moved" : true,
24495         /**
24496          * @event beforeresize
24497          * Fires before the splitter is dragged
24498          * @param {Roo.SplitBar} this
24499          */
24500         "beforeresize" : true,
24501
24502         "beforeapply" : true
24503     });
24504
24505     Roo.util.Observable.call(this);
24506 };
24507
24508 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24509     onStartProxyDrag : function(x, y){
24510         this.fireEvent("beforeresize", this);
24511         if(!this.overlay){
24512             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24513             o.unselectable();
24514             o.enableDisplayMode("block");
24515             // all splitbars share the same overlay
24516             Roo.SplitBar.prototype.overlay = o;
24517         }
24518         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24519         this.overlay.show();
24520         Roo.get(this.proxy).setDisplayed("block");
24521         var size = this.adapter.getElementSize(this);
24522         this.activeMinSize = this.getMinimumSize();;
24523         this.activeMaxSize = this.getMaximumSize();;
24524         var c1 = size - this.activeMinSize;
24525         var c2 = Math.max(this.activeMaxSize - size, 0);
24526         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24527             this.dd.resetConstraints();
24528             this.dd.setXConstraint(
24529                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24530                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24531             );
24532             this.dd.setYConstraint(0, 0);
24533         }else{
24534             this.dd.resetConstraints();
24535             this.dd.setXConstraint(0, 0);
24536             this.dd.setYConstraint(
24537                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24538                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24539             );
24540          }
24541         this.dragSpecs.startSize = size;
24542         this.dragSpecs.startPoint = [x, y];
24543         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24544     },
24545     
24546     /** 
24547      * @private Called after the drag operation by the DDProxy
24548      */
24549     onEndProxyDrag : function(e){
24550         Roo.get(this.proxy).setDisplayed(false);
24551         var endPoint = Roo.lib.Event.getXY(e);
24552         if(this.overlay){
24553             this.overlay.hide();
24554         }
24555         var newSize;
24556         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24557             newSize = this.dragSpecs.startSize + 
24558                 (this.placement == Roo.SplitBar.LEFT ?
24559                     endPoint[0] - this.dragSpecs.startPoint[0] :
24560                     this.dragSpecs.startPoint[0] - endPoint[0]
24561                 );
24562         }else{
24563             newSize = this.dragSpecs.startSize + 
24564                 (this.placement == Roo.SplitBar.TOP ?
24565                     endPoint[1] - this.dragSpecs.startPoint[1] :
24566                     this.dragSpecs.startPoint[1] - endPoint[1]
24567                 );
24568         }
24569         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24570         if(newSize != this.dragSpecs.startSize){
24571             if(this.fireEvent('beforeapply', this, newSize) !== false){
24572                 this.adapter.setElementSize(this, newSize);
24573                 this.fireEvent("moved", this, newSize);
24574                 this.fireEvent("resize", this, newSize);
24575             }
24576         }
24577     },
24578     
24579     /**
24580      * Get the adapter this SplitBar uses
24581      * @return The adapter object
24582      */
24583     getAdapter : function(){
24584         return this.adapter;
24585     },
24586     
24587     /**
24588      * Set the adapter this SplitBar uses
24589      * @param {Object} adapter A SplitBar adapter object
24590      */
24591     setAdapter : function(adapter){
24592         this.adapter = adapter;
24593         this.adapter.init(this);
24594     },
24595     
24596     /**
24597      * Gets the minimum size for the resizing element
24598      * @return {Number} The minimum size
24599      */
24600     getMinimumSize : function(){
24601         return this.minSize;
24602     },
24603     
24604     /**
24605      * Sets the minimum size for the resizing element
24606      * @param {Number} minSize The minimum size
24607      */
24608     setMinimumSize : function(minSize){
24609         this.minSize = minSize;
24610     },
24611     
24612     /**
24613      * Gets the maximum size for the resizing element
24614      * @return {Number} The maximum size
24615      */
24616     getMaximumSize : function(){
24617         return this.maxSize;
24618     },
24619     
24620     /**
24621      * Sets the maximum size for the resizing element
24622      * @param {Number} maxSize The maximum size
24623      */
24624     setMaximumSize : function(maxSize){
24625         this.maxSize = maxSize;
24626     },
24627     
24628     /**
24629      * Sets the initialize size for the resizing element
24630      * @param {Number} size The initial size
24631      */
24632     setCurrentSize : function(size){
24633         var oldAnimate = this.animate;
24634         this.animate = false;
24635         this.adapter.setElementSize(this, size);
24636         this.animate = oldAnimate;
24637     },
24638     
24639     /**
24640      * Destroy this splitbar. 
24641      * @param {Boolean} removeEl True to remove the element
24642      */
24643     destroy : function(removeEl){
24644         if(this.shim){
24645             this.shim.remove();
24646         }
24647         this.dd.unreg();
24648         this.proxy.parentNode.removeChild(this.proxy);
24649         if(removeEl){
24650             this.el.remove();
24651         }
24652     }
24653 });
24654
24655 /**
24656  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24657  */
24658 Roo.SplitBar.createProxy = function(dir){
24659     var proxy = new Roo.Element(document.createElement("div"));
24660     proxy.unselectable();
24661     var cls = 'x-splitbar-proxy';
24662     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24663     document.body.appendChild(proxy.dom);
24664     return proxy.dom;
24665 };
24666
24667 /** 
24668  * @class Roo.SplitBar.BasicLayoutAdapter
24669  * Default Adapter. It assumes the splitter and resizing element are not positioned
24670  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24671  */
24672 Roo.SplitBar.BasicLayoutAdapter = function(){
24673 };
24674
24675 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24676     // do nothing for now
24677     init : function(s){
24678     
24679     },
24680     /**
24681      * Called before drag operations to get the current size of the resizing element. 
24682      * @param {Roo.SplitBar} s The SplitBar using this adapter
24683      */
24684      getElementSize : function(s){
24685         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24686             return s.resizingEl.getWidth();
24687         }else{
24688             return s.resizingEl.getHeight();
24689         }
24690     },
24691     
24692     /**
24693      * Called after drag operations to set the size of the resizing element.
24694      * @param {Roo.SplitBar} s The SplitBar using this adapter
24695      * @param {Number} newSize The new size to set
24696      * @param {Function} onComplete A function to be invoked when resizing is complete
24697      */
24698     setElementSize : function(s, newSize, onComplete){
24699         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24700             if(!s.animate){
24701                 s.resizingEl.setWidth(newSize);
24702                 if(onComplete){
24703                     onComplete(s, newSize);
24704                 }
24705             }else{
24706                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24707             }
24708         }else{
24709             
24710             if(!s.animate){
24711                 s.resizingEl.setHeight(newSize);
24712                 if(onComplete){
24713                     onComplete(s, newSize);
24714                 }
24715             }else{
24716                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24717             }
24718         }
24719     }
24720 };
24721
24722 /** 
24723  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24724  * @extends Roo.SplitBar.BasicLayoutAdapter
24725  * Adapter that  moves the splitter element to align with the resized sizing element. 
24726  * Used with an absolute positioned SplitBar.
24727  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24728  * document.body, make sure you assign an id to the body element.
24729  */
24730 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24731     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24732     this.container = Roo.get(container);
24733 };
24734
24735 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24736     init : function(s){
24737         this.basic.init(s);
24738     },
24739     
24740     getElementSize : function(s){
24741         return this.basic.getElementSize(s);
24742     },
24743     
24744     setElementSize : function(s, newSize, onComplete){
24745         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24746     },
24747     
24748     moveSplitter : function(s){
24749         var yes = Roo.SplitBar;
24750         switch(s.placement){
24751             case yes.LEFT:
24752                 s.el.setX(s.resizingEl.getRight());
24753                 break;
24754             case yes.RIGHT:
24755                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24756                 break;
24757             case yes.TOP:
24758                 s.el.setY(s.resizingEl.getBottom());
24759                 break;
24760             case yes.BOTTOM:
24761                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24762                 break;
24763         }
24764     }
24765 };
24766
24767 /**
24768  * Orientation constant - Create a vertical SplitBar
24769  * @static
24770  * @type Number
24771  */
24772 Roo.SplitBar.VERTICAL = 1;
24773
24774 /**
24775  * Orientation constant - Create a horizontal SplitBar
24776  * @static
24777  * @type Number
24778  */
24779 Roo.SplitBar.HORIZONTAL = 2;
24780
24781 /**
24782  * Placement constant - The resizing element is to the left of the splitter element
24783  * @static
24784  * @type Number
24785  */
24786 Roo.SplitBar.LEFT = 1;
24787
24788 /**
24789  * Placement constant - The resizing element is to the right of the splitter element
24790  * @static
24791  * @type Number
24792  */
24793 Roo.SplitBar.RIGHT = 2;
24794
24795 /**
24796  * Placement constant - The resizing element is positioned above the splitter element
24797  * @static
24798  * @type Number
24799  */
24800 Roo.SplitBar.TOP = 3;
24801
24802 /**
24803  * Placement constant - The resizing element is positioned under splitter element
24804  * @static
24805  * @type Number
24806  */
24807 Roo.SplitBar.BOTTOM = 4;
24808 /*
24809  * Based on:
24810  * Ext JS Library 1.1.1
24811  * Copyright(c) 2006-2007, Ext JS, LLC.
24812  *
24813  * Originally Released Under LGPL - original licence link has changed is not relivant.
24814  *
24815  * Fork - LGPL
24816  * <script type="text/javascript">
24817  */
24818
24819 /**
24820  * @class Roo.View
24821  * @extends Roo.util.Observable
24822  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24823  * This class also supports single and multi selection modes. <br>
24824  * Create a data model bound view:
24825  <pre><code>
24826  var store = new Roo.data.Store(...);
24827
24828  var view = new Roo.View({
24829     el : "my-element",
24830     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24831  
24832     singleSelect: true,
24833     selectedClass: "ydataview-selected",
24834     store: store
24835  });
24836
24837  // listen for node click?
24838  view.on("click", function(vw, index, node, e){
24839  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24840  });
24841
24842  // load XML data
24843  dataModel.load("foobar.xml");
24844  </code></pre>
24845  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24846  * <br><br>
24847  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24848  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24849  * 
24850  * Note: old style constructor is still suported (container, template, config)
24851  * 
24852  * @constructor
24853  * Create a new View
24854  * @param {Object} config The config object
24855  * 
24856  */
24857 Roo.View = function(config, depreciated_tpl, depreciated_config){
24858     
24859     this.parent = false;
24860     
24861     if (typeof(depreciated_tpl) == 'undefined') {
24862         // new way.. - universal constructor.
24863         Roo.apply(this, config);
24864         this.el  = Roo.get(this.el);
24865     } else {
24866         // old format..
24867         this.el  = Roo.get(config);
24868         this.tpl = depreciated_tpl;
24869         Roo.apply(this, depreciated_config);
24870     }
24871     this.wrapEl  = this.el.wrap().wrap();
24872     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24873     
24874     
24875     if(typeof(this.tpl) == "string"){
24876         this.tpl = new Roo.Template(this.tpl);
24877     } else {
24878         // support xtype ctors..
24879         this.tpl = new Roo.factory(this.tpl, Roo);
24880     }
24881     
24882     
24883     this.tpl.compile();
24884     
24885     /** @private */
24886     this.addEvents({
24887         /**
24888          * @event beforeclick
24889          * Fires before a click is processed. Returns false to cancel the default action.
24890          * @param {Roo.View} this
24891          * @param {Number} index The index of the target node
24892          * @param {HTMLElement} node The target node
24893          * @param {Roo.EventObject} e The raw event object
24894          */
24895             "beforeclick" : true,
24896         /**
24897          * @event click
24898          * Fires when a template node is clicked.
24899          * @param {Roo.View} this
24900          * @param {Number} index The index of the target node
24901          * @param {HTMLElement} node The target node
24902          * @param {Roo.EventObject} e The raw event object
24903          */
24904             "click" : true,
24905         /**
24906          * @event dblclick
24907          * Fires when a template node is double clicked.
24908          * @param {Roo.View} this
24909          * @param {Number} index The index of the target node
24910          * @param {HTMLElement} node The target node
24911          * @param {Roo.EventObject} e The raw event object
24912          */
24913             "dblclick" : true,
24914         /**
24915          * @event contextmenu
24916          * Fires when a template node is right clicked.
24917          * @param {Roo.View} this
24918          * @param {Number} index The index of the target node
24919          * @param {HTMLElement} node The target node
24920          * @param {Roo.EventObject} e The raw event object
24921          */
24922             "contextmenu" : true,
24923         /**
24924          * @event selectionchange
24925          * Fires when the selected nodes change.
24926          * @param {Roo.View} this
24927          * @param {Array} selections Array of the selected nodes
24928          */
24929             "selectionchange" : true,
24930     
24931         /**
24932          * @event beforeselect
24933          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24934          * @param {Roo.View} this
24935          * @param {HTMLElement} node The node to be selected
24936          * @param {Array} selections Array of currently selected nodes
24937          */
24938             "beforeselect" : true,
24939         /**
24940          * @event preparedata
24941          * Fires on every row to render, to allow you to change the data.
24942          * @param {Roo.View} this
24943          * @param {Object} data to be rendered (change this)
24944          */
24945           "preparedata" : true
24946           
24947           
24948         });
24949
24950
24951
24952     this.el.on({
24953         "click": this.onClick,
24954         "dblclick": this.onDblClick,
24955         "contextmenu": this.onContextMenu,
24956         scope:this
24957     });
24958
24959     this.selections = [];
24960     this.nodes = [];
24961     this.cmp = new Roo.CompositeElementLite([]);
24962     if(this.store){
24963         this.store = Roo.factory(this.store, Roo.data);
24964         this.setStore(this.store, true);
24965     }
24966     
24967     if ( this.footer && this.footer.xtype) {
24968            
24969          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24970         
24971         this.footer.dataSource = this.store
24972         this.footer.container = fctr;
24973         this.footer = Roo.factory(this.footer, Roo);
24974         fctr.insertFirst(this.el);
24975         
24976         // this is a bit insane - as the paging toolbar seems to detach the el..
24977 //        dom.parentNode.parentNode.parentNode
24978          // they get detached?
24979     }
24980     
24981     
24982     Roo.View.superclass.constructor.call(this);
24983     
24984     
24985 };
24986
24987 Roo.extend(Roo.View, Roo.util.Observable, {
24988     
24989      /**
24990      * @cfg {Roo.data.Store} store Data store to load data from.
24991      */
24992     store : false,
24993     
24994     /**
24995      * @cfg {String|Roo.Element} el The container element.
24996      */
24997     el : '',
24998     
24999     /**
25000      * @cfg {String|Roo.Template} tpl The template used by this View 
25001      */
25002     tpl : false,
25003     /**
25004      * @cfg {String} dataName the named area of the template to use as the data area
25005      *                          Works with domtemplates roo-name="name"
25006      */
25007     dataName: false,
25008     /**
25009      * @cfg {String} selectedClass The css class to add to selected nodes
25010      */
25011     selectedClass : "x-view-selected",
25012      /**
25013      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25014      */
25015     emptyText : "",
25016     
25017     /**
25018      * @cfg {String} text to display on mask (default Loading)
25019      */
25020     mask : false,
25021     /**
25022      * @cfg {Boolean} multiSelect Allow multiple selection
25023      */
25024     multiSelect : false,
25025     /**
25026      * @cfg {Boolean} singleSelect Allow single selection
25027      */
25028     singleSelect:  false,
25029     
25030     /**
25031      * @cfg {Boolean} toggleSelect - selecting 
25032      */
25033     toggleSelect : false,
25034     
25035     /**
25036      * @cfg {Boolean} tickable - selecting 
25037      */
25038     tickable : false,
25039     
25040     /**
25041      * Returns the element this view is bound to.
25042      * @return {Roo.Element}
25043      */
25044     getEl : function(){
25045         return this.wrapEl;
25046     },
25047     
25048     
25049
25050     /**
25051      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25052      */
25053     refresh : function(){
25054         Roo.log('refresh');
25055         var t = this.tpl;
25056         
25057         // if we are using something like 'domtemplate', then
25058         // the what gets used is:
25059         // t.applySubtemplate(NAME, data, wrapping data..)
25060         // the outer template then get' applied with
25061         //     the store 'extra data'
25062         // and the body get's added to the
25063         //      roo-name="data" node?
25064         //      <span class='roo-tpl-{name}'></span> ?????
25065         
25066         
25067         
25068         this.clearSelections();
25069         this.el.update("");
25070         var html = [];
25071         var records = this.store.getRange();
25072         if(records.length < 1) {
25073             
25074             // is this valid??  = should it render a template??
25075             
25076             this.el.update(this.emptyText);
25077             return;
25078         }
25079         var el = this.el;
25080         if (this.dataName) {
25081             this.el.update(t.apply(this.store.meta)); //????
25082             el = this.el.child('.roo-tpl-' + this.dataName);
25083         }
25084         
25085         for(var i = 0, len = records.length; i < len; i++){
25086             var data = this.prepareData(records[i].data, i, records[i]);
25087             this.fireEvent("preparedata", this, data, i, records[i]);
25088             
25089             var d = Roo.apply({}, data);
25090             
25091             if(this.tickable){
25092                 Roo.apply(d, {'roo-id' : Roo.id()});
25093                 
25094                 var _this = this;
25095             
25096                 Roo.each(this.parent.item, function(item){
25097                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25098                         return;
25099                     }
25100                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25101                 });
25102             }
25103             
25104             html[html.length] = Roo.util.Format.trim(
25105                 this.dataName ?
25106                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25107                     t.apply(d)
25108             );
25109         }
25110         
25111         
25112         
25113         el.update(html.join(""));
25114         this.nodes = el.dom.childNodes;
25115         this.updateIndexes(0);
25116     },
25117     
25118
25119     /**
25120      * Function to override to reformat the data that is sent to
25121      * the template for each node.
25122      * DEPRICATED - use the preparedata event handler.
25123      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25124      * a JSON object for an UpdateManager bound view).
25125      */
25126     prepareData : function(data, index, record)
25127     {
25128         this.fireEvent("preparedata", this, data, index, record);
25129         return data;
25130     },
25131
25132     onUpdate : function(ds, record){
25133          Roo.log('on update');   
25134         this.clearSelections();
25135         var index = this.store.indexOf(record);
25136         var n = this.nodes[index];
25137         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25138         n.parentNode.removeChild(n);
25139         this.updateIndexes(index, index);
25140     },
25141
25142     
25143     
25144 // --------- FIXME     
25145     onAdd : function(ds, records, index)
25146     {
25147         Roo.log(['on Add', ds, records, index] );        
25148         this.clearSelections();
25149         if(this.nodes.length == 0){
25150             this.refresh();
25151             return;
25152         }
25153         var n = this.nodes[index];
25154         for(var i = 0, len = records.length; i < len; i++){
25155             var d = this.prepareData(records[i].data, i, records[i]);
25156             if(n){
25157                 this.tpl.insertBefore(n, d);
25158             }else{
25159                 
25160                 this.tpl.append(this.el, d);
25161             }
25162         }
25163         this.updateIndexes(index);
25164     },
25165
25166     onRemove : function(ds, record, index){
25167         Roo.log('onRemove');
25168         this.clearSelections();
25169         var el = this.dataName  ?
25170             this.el.child('.roo-tpl-' + this.dataName) :
25171             this.el; 
25172         
25173         el.dom.removeChild(this.nodes[index]);
25174         this.updateIndexes(index);
25175     },
25176
25177     /**
25178      * Refresh an individual node.
25179      * @param {Number} index
25180      */
25181     refreshNode : function(index){
25182         this.onUpdate(this.store, this.store.getAt(index));
25183     },
25184
25185     updateIndexes : function(startIndex, endIndex){
25186         var ns = this.nodes;
25187         startIndex = startIndex || 0;
25188         endIndex = endIndex || ns.length - 1;
25189         for(var i = startIndex; i <= endIndex; i++){
25190             ns[i].nodeIndex = i;
25191         }
25192     },
25193
25194     /**
25195      * Changes the data store this view uses and refresh the view.
25196      * @param {Store} store
25197      */
25198     setStore : function(store, initial){
25199         if(!initial && this.store){
25200             this.store.un("datachanged", this.refresh);
25201             this.store.un("add", this.onAdd);
25202             this.store.un("remove", this.onRemove);
25203             this.store.un("update", this.onUpdate);
25204             this.store.un("clear", this.refresh);
25205             this.store.un("beforeload", this.onBeforeLoad);
25206             this.store.un("load", this.onLoad);
25207             this.store.un("loadexception", this.onLoad);
25208         }
25209         if(store){
25210           
25211             store.on("datachanged", this.refresh, this);
25212             store.on("add", this.onAdd, this);
25213             store.on("remove", this.onRemove, this);
25214             store.on("update", this.onUpdate, this);
25215             store.on("clear", this.refresh, this);
25216             store.on("beforeload", this.onBeforeLoad, this);
25217             store.on("load", this.onLoad, this);
25218             store.on("loadexception", this.onLoad, this);
25219         }
25220         
25221         if(store){
25222             this.refresh();
25223         }
25224     },
25225     /**
25226      * onbeforeLoad - masks the loading area.
25227      *
25228      */
25229     onBeforeLoad : function(store,opts)
25230     {
25231          Roo.log('onBeforeLoad');   
25232         if (!opts.add) {
25233             this.el.update("");
25234         }
25235         this.el.mask(this.mask ? this.mask : "Loading" ); 
25236     },
25237     onLoad : function ()
25238     {
25239         this.el.unmask();
25240     },
25241     
25242
25243     /**
25244      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25245      * @param {HTMLElement} node
25246      * @return {HTMLElement} The template node
25247      */
25248     findItemFromChild : function(node){
25249         var el = this.dataName  ?
25250             this.el.child('.roo-tpl-' + this.dataName,true) :
25251             this.el.dom; 
25252         
25253         if(!node || node.parentNode == el){
25254                     return node;
25255             }
25256             var p = node.parentNode;
25257             while(p && p != el){
25258             if(p.parentNode == el){
25259                 return p;
25260             }
25261             p = p.parentNode;
25262         }
25263             return null;
25264     },
25265
25266     /** @ignore */
25267     onClick : function(e){
25268         var item = this.findItemFromChild(e.getTarget());
25269         if(item){
25270             var index = this.indexOf(item);
25271             if(this.onItemClick(item, index, e) !== false){
25272                 this.fireEvent("click", this, index, item, e);
25273             }
25274         }else{
25275             this.clearSelections();
25276         }
25277     },
25278
25279     /** @ignore */
25280     onContextMenu : function(e){
25281         var item = this.findItemFromChild(e.getTarget());
25282         if(item){
25283             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25284         }
25285     },
25286
25287     /** @ignore */
25288     onDblClick : function(e){
25289         var item = this.findItemFromChild(e.getTarget());
25290         if(item){
25291             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25292         }
25293     },
25294
25295     onItemClick : function(item, index, e)
25296     {
25297         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25298             return false;
25299         }
25300         if (this.toggleSelect) {
25301             var m = this.isSelected(item) ? 'unselect' : 'select';
25302             Roo.log(m);
25303             var _t = this;
25304             _t[m](item, true, false);
25305             return true;
25306         }
25307         if(this.multiSelect || this.singleSelect){
25308             if(this.multiSelect && e.shiftKey && this.lastSelection){
25309                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25310             }else{
25311                 this.select(item, this.multiSelect && e.ctrlKey);
25312                 this.lastSelection = item;
25313             }
25314             
25315             if(!this.tickable){
25316                 e.preventDefault();
25317             }
25318             
25319         }
25320         return true;
25321     },
25322
25323     /**
25324      * Get the number of selected nodes.
25325      * @return {Number}
25326      */
25327     getSelectionCount : function(){
25328         return this.selections.length;
25329     },
25330
25331     /**
25332      * Get the currently selected nodes.
25333      * @return {Array} An array of HTMLElements
25334      */
25335     getSelectedNodes : function(){
25336         return this.selections;
25337     },
25338
25339     /**
25340      * Get the indexes of the selected nodes.
25341      * @return {Array}
25342      */
25343     getSelectedIndexes : function(){
25344         var indexes = [], s = this.selections;
25345         for(var i = 0, len = s.length; i < len; i++){
25346             indexes.push(s[i].nodeIndex);
25347         }
25348         return indexes;
25349     },
25350
25351     /**
25352      * Clear all selections
25353      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25354      */
25355     clearSelections : function(suppressEvent){
25356         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25357             this.cmp.elements = this.selections;
25358             this.cmp.removeClass(this.selectedClass);
25359             this.selections = [];
25360             if(!suppressEvent){
25361                 this.fireEvent("selectionchange", this, this.selections);
25362             }
25363         }
25364     },
25365
25366     /**
25367      * Returns true if the passed node is selected
25368      * @param {HTMLElement/Number} node The node or node index
25369      * @return {Boolean}
25370      */
25371     isSelected : function(node){
25372         var s = this.selections;
25373         if(s.length < 1){
25374             return false;
25375         }
25376         node = this.getNode(node);
25377         return s.indexOf(node) !== -1;
25378     },
25379
25380     /**
25381      * Selects nodes.
25382      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25383      * @param {Boolean} keepExisting (optional) true to keep existing selections
25384      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25385      */
25386     select : function(nodeInfo, keepExisting, suppressEvent){
25387         if(nodeInfo instanceof Array){
25388             if(!keepExisting){
25389                 this.clearSelections(true);
25390             }
25391             for(var i = 0, len = nodeInfo.length; i < len; i++){
25392                 this.select(nodeInfo[i], true, true);
25393             }
25394             return;
25395         } 
25396         var node = this.getNode(nodeInfo);
25397         if(!node || this.isSelected(node)){
25398             return; // already selected.
25399         }
25400         if(!keepExisting){
25401             this.clearSelections(true);
25402         }
25403         
25404         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25405             Roo.fly(node).addClass(this.selectedClass);
25406             this.selections.push(node);
25407             if(!suppressEvent){
25408                 this.fireEvent("selectionchange", this, this.selections);
25409             }
25410         }
25411         
25412         
25413     },
25414       /**
25415      * Unselects nodes.
25416      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25417      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25418      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25419      */
25420     unselect : function(nodeInfo, keepExisting, suppressEvent)
25421     {
25422         if(nodeInfo instanceof Array){
25423             Roo.each(this.selections, function(s) {
25424                 this.unselect(s, nodeInfo);
25425             }, this);
25426             return;
25427         }
25428         var node = this.getNode(nodeInfo);
25429         if(!node || !this.isSelected(node)){
25430             Roo.log("not selected");
25431             return; // not selected.
25432         }
25433         // fireevent???
25434         var ns = [];
25435         Roo.each(this.selections, function(s) {
25436             if (s == node ) {
25437                 Roo.fly(node).removeClass(this.selectedClass);
25438
25439                 return;
25440             }
25441             ns.push(s);
25442         },this);
25443         
25444         this.selections= ns;
25445         this.fireEvent("selectionchange", this, this.selections);
25446     },
25447
25448     /**
25449      * Gets a template node.
25450      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25451      * @return {HTMLElement} The node or null if it wasn't found
25452      */
25453     getNode : function(nodeInfo){
25454         if(typeof nodeInfo == "string"){
25455             return document.getElementById(nodeInfo);
25456         }else if(typeof nodeInfo == "number"){
25457             return this.nodes[nodeInfo];
25458         }
25459         return nodeInfo;
25460     },
25461
25462     /**
25463      * Gets a range template nodes.
25464      * @param {Number} startIndex
25465      * @param {Number} endIndex
25466      * @return {Array} An array of nodes
25467      */
25468     getNodes : function(start, end){
25469         var ns = this.nodes;
25470         start = start || 0;
25471         end = typeof end == "undefined" ? ns.length - 1 : end;
25472         var nodes = [];
25473         if(start <= end){
25474             for(var i = start; i <= end; i++){
25475                 nodes.push(ns[i]);
25476             }
25477         } else{
25478             for(var i = start; i >= end; i--){
25479                 nodes.push(ns[i]);
25480             }
25481         }
25482         return nodes;
25483     },
25484
25485     /**
25486      * Finds the index of the passed node
25487      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25488      * @return {Number} The index of the node or -1
25489      */
25490     indexOf : function(node){
25491         node = this.getNode(node);
25492         if(typeof node.nodeIndex == "number"){
25493             return node.nodeIndex;
25494         }
25495         var ns = this.nodes;
25496         for(var i = 0, len = ns.length; i < len; i++){
25497             if(ns[i] == node){
25498                 return i;
25499             }
25500         }
25501         return -1;
25502     }
25503 });
25504 /*
25505  * Based on:
25506  * Ext JS Library 1.1.1
25507  * Copyright(c) 2006-2007, Ext JS, LLC.
25508  *
25509  * Originally Released Under LGPL - original licence link has changed is not relivant.
25510  *
25511  * Fork - LGPL
25512  * <script type="text/javascript">
25513  */
25514
25515 /**
25516  * @class Roo.JsonView
25517  * @extends Roo.View
25518  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25519 <pre><code>
25520 var view = new Roo.JsonView({
25521     container: "my-element",
25522     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25523     multiSelect: true, 
25524     jsonRoot: "data" 
25525 });
25526
25527 // listen for node click?
25528 view.on("click", function(vw, index, node, e){
25529     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25530 });
25531
25532 // direct load of JSON data
25533 view.load("foobar.php");
25534
25535 // Example from my blog list
25536 var tpl = new Roo.Template(
25537     '&lt;div class="entry"&gt;' +
25538     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25539     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25540     "&lt;/div&gt;&lt;hr /&gt;"
25541 );
25542
25543 var moreView = new Roo.JsonView({
25544     container :  "entry-list", 
25545     template : tpl,
25546     jsonRoot: "posts"
25547 });
25548 moreView.on("beforerender", this.sortEntries, this);
25549 moreView.load({
25550     url: "/blog/get-posts.php",
25551     params: "allposts=true",
25552     text: "Loading Blog Entries..."
25553 });
25554 </code></pre>
25555
25556 * Note: old code is supported with arguments : (container, template, config)
25557
25558
25559  * @constructor
25560  * Create a new JsonView
25561  * 
25562  * @param {Object} config The config object
25563  * 
25564  */
25565 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25566     
25567     
25568     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25569
25570     var um = this.el.getUpdateManager();
25571     um.setRenderer(this);
25572     um.on("update", this.onLoad, this);
25573     um.on("failure", this.onLoadException, this);
25574
25575     /**
25576      * @event beforerender
25577      * Fires before rendering of the downloaded JSON data.
25578      * @param {Roo.JsonView} this
25579      * @param {Object} data The JSON data loaded
25580      */
25581     /**
25582      * @event load
25583      * Fires when data is loaded.
25584      * @param {Roo.JsonView} this
25585      * @param {Object} data The JSON data loaded
25586      * @param {Object} response The raw Connect response object
25587      */
25588     /**
25589      * @event loadexception
25590      * Fires when loading fails.
25591      * @param {Roo.JsonView} this
25592      * @param {Object} response The raw Connect response object
25593      */
25594     this.addEvents({
25595         'beforerender' : true,
25596         'load' : true,
25597         'loadexception' : true
25598     });
25599 };
25600 Roo.extend(Roo.JsonView, Roo.View, {
25601     /**
25602      * @type {String} The root property in the loaded JSON object that contains the data
25603      */
25604     jsonRoot : "",
25605
25606     /**
25607      * Refreshes the view.
25608      */
25609     refresh : function(){
25610         this.clearSelections();
25611         this.el.update("");
25612         var html = [];
25613         var o = this.jsonData;
25614         if(o && o.length > 0){
25615             for(var i = 0, len = o.length; i < len; i++){
25616                 var data = this.prepareData(o[i], i, o);
25617                 html[html.length] = this.tpl.apply(data);
25618             }
25619         }else{
25620             html.push(this.emptyText);
25621         }
25622         this.el.update(html.join(""));
25623         this.nodes = this.el.dom.childNodes;
25624         this.updateIndexes(0);
25625     },
25626
25627     /**
25628      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25629      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25630      <pre><code>
25631      view.load({
25632          url: "your-url.php",
25633          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25634          callback: yourFunction,
25635          scope: yourObject, //(optional scope)
25636          discardUrl: false,
25637          nocache: false,
25638          text: "Loading...",
25639          timeout: 30,
25640          scripts: false
25641      });
25642      </code></pre>
25643      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25644      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25645      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25646      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25647      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25648      */
25649     load : function(){
25650         var um = this.el.getUpdateManager();
25651         um.update.apply(um, arguments);
25652     },
25653
25654     render : function(el, response){
25655         this.clearSelections();
25656         this.el.update("");
25657         var o;
25658         try{
25659             o = Roo.util.JSON.decode(response.responseText);
25660             if(this.jsonRoot){
25661                 
25662                 o = o[this.jsonRoot];
25663             }
25664         } catch(e){
25665         }
25666         /**
25667          * The current JSON data or null
25668          */
25669         this.jsonData = o;
25670         this.beforeRender();
25671         this.refresh();
25672     },
25673
25674 /**
25675  * Get the number of records in the current JSON dataset
25676  * @return {Number}
25677  */
25678     getCount : function(){
25679         return this.jsonData ? this.jsonData.length : 0;
25680     },
25681
25682 /**
25683  * Returns the JSON object for the specified node(s)
25684  * @param {HTMLElement/Array} node The node or an array of nodes
25685  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25686  * you get the JSON object for the node
25687  */
25688     getNodeData : function(node){
25689         if(node instanceof Array){
25690             var data = [];
25691             for(var i = 0, len = node.length; i < len; i++){
25692                 data.push(this.getNodeData(node[i]));
25693             }
25694             return data;
25695         }
25696         return this.jsonData[this.indexOf(node)] || null;
25697     },
25698
25699     beforeRender : function(){
25700         this.snapshot = this.jsonData;
25701         if(this.sortInfo){
25702             this.sort.apply(this, this.sortInfo);
25703         }
25704         this.fireEvent("beforerender", this, this.jsonData);
25705     },
25706
25707     onLoad : function(el, o){
25708         this.fireEvent("load", this, this.jsonData, o);
25709     },
25710
25711     onLoadException : function(el, o){
25712         this.fireEvent("loadexception", this, o);
25713     },
25714
25715 /**
25716  * Filter the data by a specific property.
25717  * @param {String} property A property on your JSON objects
25718  * @param {String/RegExp} value Either string that the property values
25719  * should start with, or a RegExp to test against the property
25720  */
25721     filter : function(property, value){
25722         if(this.jsonData){
25723             var data = [];
25724             var ss = this.snapshot;
25725             if(typeof value == "string"){
25726                 var vlen = value.length;
25727                 if(vlen == 0){
25728                     this.clearFilter();
25729                     return;
25730                 }
25731                 value = value.toLowerCase();
25732                 for(var i = 0, len = ss.length; i < len; i++){
25733                     var o = ss[i];
25734                     if(o[property].substr(0, vlen).toLowerCase() == value){
25735                         data.push(o);
25736                     }
25737                 }
25738             } else if(value.exec){ // regex?
25739                 for(var i = 0, len = ss.length; i < len; i++){
25740                     var o = ss[i];
25741                     if(value.test(o[property])){
25742                         data.push(o);
25743                     }
25744                 }
25745             } else{
25746                 return;
25747             }
25748             this.jsonData = data;
25749             this.refresh();
25750         }
25751     },
25752
25753 /**
25754  * Filter by a function. The passed function will be called with each
25755  * object in the current dataset. If the function returns true the value is kept,
25756  * otherwise it is filtered.
25757  * @param {Function} fn
25758  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25759  */
25760     filterBy : function(fn, scope){
25761         if(this.jsonData){
25762             var data = [];
25763             var ss = this.snapshot;
25764             for(var i = 0, len = ss.length; i < len; i++){
25765                 var o = ss[i];
25766                 if(fn.call(scope || this, o)){
25767                     data.push(o);
25768                 }
25769             }
25770             this.jsonData = data;
25771             this.refresh();
25772         }
25773     },
25774
25775 /**
25776  * Clears the current filter.
25777  */
25778     clearFilter : function(){
25779         if(this.snapshot && this.jsonData != this.snapshot){
25780             this.jsonData = this.snapshot;
25781             this.refresh();
25782         }
25783     },
25784
25785
25786 /**
25787  * Sorts the data for this view and refreshes it.
25788  * @param {String} property A property on your JSON objects to sort on
25789  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25790  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25791  */
25792     sort : function(property, dir, sortType){
25793         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25794         if(this.jsonData){
25795             var p = property;
25796             var dsc = dir && dir.toLowerCase() == "desc";
25797             var f = function(o1, o2){
25798                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25799                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25800                 ;
25801                 if(v1 < v2){
25802                     return dsc ? +1 : -1;
25803                 } else if(v1 > v2){
25804                     return dsc ? -1 : +1;
25805                 } else{
25806                     return 0;
25807                 }
25808             };
25809             this.jsonData.sort(f);
25810             this.refresh();
25811             if(this.jsonData != this.snapshot){
25812                 this.snapshot.sort(f);
25813             }
25814         }
25815     }
25816 });/*
25817  * Based on:
25818  * Ext JS Library 1.1.1
25819  * Copyright(c) 2006-2007, Ext JS, LLC.
25820  *
25821  * Originally Released Under LGPL - original licence link has changed is not relivant.
25822  *
25823  * Fork - LGPL
25824  * <script type="text/javascript">
25825  */
25826  
25827
25828 /**
25829  * @class Roo.ColorPalette
25830  * @extends Roo.Component
25831  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25832  * Here's an example of typical usage:
25833  * <pre><code>
25834 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25835 cp.render('my-div');
25836
25837 cp.on('select', function(palette, selColor){
25838     // do something with selColor
25839 });
25840 </code></pre>
25841  * @constructor
25842  * Create a new ColorPalette
25843  * @param {Object} config The config object
25844  */
25845 Roo.ColorPalette = function(config){
25846     Roo.ColorPalette.superclass.constructor.call(this, config);
25847     this.addEvents({
25848         /**
25849              * @event select
25850              * Fires when a color is selected
25851              * @param {ColorPalette} this
25852              * @param {String} color The 6-digit color hex code (without the # symbol)
25853              */
25854         select: true
25855     });
25856
25857     if(this.handler){
25858         this.on("select", this.handler, this.scope, true);
25859     }
25860 };
25861 Roo.extend(Roo.ColorPalette, Roo.Component, {
25862     /**
25863      * @cfg {String} itemCls
25864      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25865      */
25866     itemCls : "x-color-palette",
25867     /**
25868      * @cfg {String} value
25869      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25870      * the hex codes are case-sensitive.
25871      */
25872     value : null,
25873     clickEvent:'click',
25874     // private
25875     ctype: "Roo.ColorPalette",
25876
25877     /**
25878      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25879      */
25880     allowReselect : false,
25881
25882     /**
25883      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25884      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25885      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25886      * of colors with the width setting until the box is symmetrical.</p>
25887      * <p>You can override individual colors if needed:</p>
25888      * <pre><code>
25889 var cp = new Roo.ColorPalette();
25890 cp.colors[0] = "FF0000";  // change the first box to red
25891 </code></pre>
25892
25893 Or you can provide a custom array of your own for complete control:
25894 <pre><code>
25895 var cp = new Roo.ColorPalette();
25896 cp.colors = ["000000", "993300", "333300"];
25897 </code></pre>
25898      * @type Array
25899      */
25900     colors : [
25901         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25902         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25903         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25904         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25905         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25906     ],
25907
25908     // private
25909     onRender : function(container, position){
25910         var t = new Roo.MasterTemplate(
25911             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25912         );
25913         var c = this.colors;
25914         for(var i = 0, len = c.length; i < len; i++){
25915             t.add([c[i]]);
25916         }
25917         var el = document.createElement("div");
25918         el.className = this.itemCls;
25919         t.overwrite(el);
25920         container.dom.insertBefore(el, position);
25921         this.el = Roo.get(el);
25922         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25923         if(this.clickEvent != 'click'){
25924             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25925         }
25926     },
25927
25928     // private
25929     afterRender : function(){
25930         Roo.ColorPalette.superclass.afterRender.call(this);
25931         if(this.value){
25932             var s = this.value;
25933             this.value = null;
25934             this.select(s);
25935         }
25936     },
25937
25938     // private
25939     handleClick : function(e, t){
25940         e.preventDefault();
25941         if(!this.disabled){
25942             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25943             this.select(c.toUpperCase());
25944         }
25945     },
25946
25947     /**
25948      * Selects the specified color in the palette (fires the select event)
25949      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25950      */
25951     select : function(color){
25952         color = color.replace("#", "");
25953         if(color != this.value || this.allowReselect){
25954             var el = this.el;
25955             if(this.value){
25956                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25957             }
25958             el.child("a.color-"+color).addClass("x-color-palette-sel");
25959             this.value = color;
25960             this.fireEvent("select", this, color);
25961         }
25962     }
25963 });/*
25964  * Based on:
25965  * Ext JS Library 1.1.1
25966  * Copyright(c) 2006-2007, Ext JS, LLC.
25967  *
25968  * Originally Released Under LGPL - original licence link has changed is not relivant.
25969  *
25970  * Fork - LGPL
25971  * <script type="text/javascript">
25972  */
25973  
25974 /**
25975  * @class Roo.DatePicker
25976  * @extends Roo.Component
25977  * Simple date picker class.
25978  * @constructor
25979  * Create a new DatePicker
25980  * @param {Object} config The config object
25981  */
25982 Roo.DatePicker = function(config){
25983     Roo.DatePicker.superclass.constructor.call(this, config);
25984
25985     this.value = config && config.value ?
25986                  config.value.clearTime() : new Date().clearTime();
25987
25988     this.addEvents({
25989         /**
25990              * @event select
25991              * Fires when a date is selected
25992              * @param {DatePicker} this
25993              * @param {Date} date The selected date
25994              */
25995         'select': true,
25996         /**
25997              * @event monthchange
25998              * Fires when the displayed month changes 
25999              * @param {DatePicker} this
26000              * @param {Date} date The selected month
26001              */
26002         'monthchange': true
26003     });
26004
26005     if(this.handler){
26006         this.on("select", this.handler,  this.scope || this);
26007     }
26008     // build the disabledDatesRE
26009     if(!this.disabledDatesRE && this.disabledDates){
26010         var dd = this.disabledDates;
26011         var re = "(?:";
26012         for(var i = 0; i < dd.length; i++){
26013             re += dd[i];
26014             if(i != dd.length-1) re += "|";
26015         }
26016         this.disabledDatesRE = new RegExp(re + ")");
26017     }
26018 };
26019
26020 Roo.extend(Roo.DatePicker, Roo.Component, {
26021     /**
26022      * @cfg {String} todayText
26023      * The text to display on the button that selects the current date (defaults to "Today")
26024      */
26025     todayText : "Today",
26026     /**
26027      * @cfg {String} okText
26028      * The text to display on the ok button
26029      */
26030     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26031     /**
26032      * @cfg {String} cancelText
26033      * The text to display on the cancel button
26034      */
26035     cancelText : "Cancel",
26036     /**
26037      * @cfg {String} todayTip
26038      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26039      */
26040     todayTip : "{0} (Spacebar)",
26041     /**
26042      * @cfg {Date} minDate
26043      * Minimum allowable date (JavaScript date object, defaults to null)
26044      */
26045     minDate : null,
26046     /**
26047      * @cfg {Date} maxDate
26048      * Maximum allowable date (JavaScript date object, defaults to null)
26049      */
26050     maxDate : null,
26051     /**
26052      * @cfg {String} minText
26053      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26054      */
26055     minText : "This date is before the minimum date",
26056     /**
26057      * @cfg {String} maxText
26058      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26059      */
26060     maxText : "This date is after the maximum date",
26061     /**
26062      * @cfg {String} format
26063      * The default date format string which can be overriden for localization support.  The format must be
26064      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26065      */
26066     format : "m/d/y",
26067     /**
26068      * @cfg {Array} disabledDays
26069      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26070      */
26071     disabledDays : null,
26072     /**
26073      * @cfg {String} disabledDaysText
26074      * The tooltip to display when the date falls on a disabled day (defaults to "")
26075      */
26076     disabledDaysText : "",
26077     /**
26078      * @cfg {RegExp} disabledDatesRE
26079      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26080      */
26081     disabledDatesRE : null,
26082     /**
26083      * @cfg {String} disabledDatesText
26084      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26085      */
26086     disabledDatesText : "",
26087     /**
26088      * @cfg {Boolean} constrainToViewport
26089      * True to constrain the date picker to the viewport (defaults to true)
26090      */
26091     constrainToViewport : true,
26092     /**
26093      * @cfg {Array} monthNames
26094      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26095      */
26096     monthNames : Date.monthNames,
26097     /**
26098      * @cfg {Array} dayNames
26099      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26100      */
26101     dayNames : Date.dayNames,
26102     /**
26103      * @cfg {String} nextText
26104      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26105      */
26106     nextText: 'Next Month (Control+Right)',
26107     /**
26108      * @cfg {String} prevText
26109      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26110      */
26111     prevText: 'Previous Month (Control+Left)',
26112     /**
26113      * @cfg {String} monthYearText
26114      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26115      */
26116     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26117     /**
26118      * @cfg {Number} startDay
26119      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26120      */
26121     startDay : 0,
26122     /**
26123      * @cfg {Bool} showClear
26124      * Show a clear button (usefull for date form elements that can be blank.)
26125      */
26126     
26127     showClear: false,
26128     
26129     /**
26130      * Sets the value of the date field
26131      * @param {Date} value The date to set
26132      */
26133     setValue : function(value){
26134         var old = this.value;
26135         
26136         if (typeof(value) == 'string') {
26137          
26138             value = Date.parseDate(value, this.format);
26139         }
26140         if (!value) {
26141             value = new Date();
26142         }
26143         
26144         this.value = value.clearTime(true);
26145         if(this.el){
26146             this.update(this.value);
26147         }
26148     },
26149
26150     /**
26151      * Gets the current selected value of the date field
26152      * @return {Date} The selected date
26153      */
26154     getValue : function(){
26155         return this.value;
26156     },
26157
26158     // private
26159     focus : function(){
26160         if(this.el){
26161             this.update(this.activeDate);
26162         }
26163     },
26164
26165     // privateval
26166     onRender : function(container, position){
26167         
26168         var m = [
26169              '<table cellspacing="0">',
26170                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26171                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26172         var dn = this.dayNames;
26173         for(var i = 0; i < 7; i++){
26174             var d = this.startDay+i;
26175             if(d > 6){
26176                 d = d-7;
26177             }
26178             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26179         }
26180         m[m.length] = "</tr></thead><tbody><tr>";
26181         for(var i = 0; i < 42; i++) {
26182             if(i % 7 == 0 && i != 0){
26183                 m[m.length] = "</tr><tr>";
26184             }
26185             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26186         }
26187         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26188             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26189
26190         var el = document.createElement("div");
26191         el.className = "x-date-picker";
26192         el.innerHTML = m.join("");
26193
26194         container.dom.insertBefore(el, position);
26195
26196         this.el = Roo.get(el);
26197         this.eventEl = Roo.get(el.firstChild);
26198
26199         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26200             handler: this.showPrevMonth,
26201             scope: this,
26202             preventDefault:true,
26203             stopDefault:true
26204         });
26205
26206         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26207             handler: this.showNextMonth,
26208             scope: this,
26209             preventDefault:true,
26210             stopDefault:true
26211         });
26212
26213         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26214
26215         this.monthPicker = this.el.down('div.x-date-mp');
26216         this.monthPicker.enableDisplayMode('block');
26217         
26218         var kn = new Roo.KeyNav(this.eventEl, {
26219             "left" : function(e){
26220                 e.ctrlKey ?
26221                     this.showPrevMonth() :
26222                     this.update(this.activeDate.add("d", -1));
26223             },
26224
26225             "right" : function(e){
26226                 e.ctrlKey ?
26227                     this.showNextMonth() :
26228                     this.update(this.activeDate.add("d", 1));
26229             },
26230
26231             "up" : function(e){
26232                 e.ctrlKey ?
26233                     this.showNextYear() :
26234                     this.update(this.activeDate.add("d", -7));
26235             },
26236
26237             "down" : function(e){
26238                 e.ctrlKey ?
26239                     this.showPrevYear() :
26240                     this.update(this.activeDate.add("d", 7));
26241             },
26242
26243             "pageUp" : function(e){
26244                 this.showNextMonth();
26245             },
26246
26247             "pageDown" : function(e){
26248                 this.showPrevMonth();
26249             },
26250
26251             "enter" : function(e){
26252                 e.stopPropagation();
26253                 return true;
26254             },
26255
26256             scope : this
26257         });
26258
26259         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26260
26261         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26262
26263         this.el.unselectable();
26264         
26265         this.cells = this.el.select("table.x-date-inner tbody td");
26266         this.textNodes = this.el.query("table.x-date-inner tbody span");
26267
26268         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26269             text: "&#160;",
26270             tooltip: this.monthYearText
26271         });
26272
26273         this.mbtn.on('click', this.showMonthPicker, this);
26274         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26275
26276
26277         var today = (new Date()).dateFormat(this.format);
26278         
26279         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26280         if (this.showClear) {
26281             baseTb.add( new Roo.Toolbar.Fill());
26282         }
26283         baseTb.add({
26284             text: String.format(this.todayText, today),
26285             tooltip: String.format(this.todayTip, today),
26286             handler: this.selectToday,
26287             scope: this
26288         });
26289         
26290         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26291             
26292         //});
26293         if (this.showClear) {
26294             
26295             baseTb.add( new Roo.Toolbar.Fill());
26296             baseTb.add({
26297                 text: '&#160;',
26298                 cls: 'x-btn-icon x-btn-clear',
26299                 handler: function() {
26300                     //this.value = '';
26301                     this.fireEvent("select", this, '');
26302                 },
26303                 scope: this
26304             });
26305         }
26306         
26307         
26308         if(Roo.isIE){
26309             this.el.repaint();
26310         }
26311         this.update(this.value);
26312     },
26313
26314     createMonthPicker : function(){
26315         if(!this.monthPicker.dom.firstChild){
26316             var buf = ['<table border="0" cellspacing="0">'];
26317             for(var i = 0; i < 6; i++){
26318                 buf.push(
26319                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26320                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26321                     i == 0 ?
26322                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26323                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26324                 );
26325             }
26326             buf.push(
26327                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26328                     this.okText,
26329                     '</button><button type="button" class="x-date-mp-cancel">',
26330                     this.cancelText,
26331                     '</button></td></tr>',
26332                 '</table>'
26333             );
26334             this.monthPicker.update(buf.join(''));
26335             this.monthPicker.on('click', this.onMonthClick, this);
26336             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26337
26338             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26339             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26340
26341             this.mpMonths.each(function(m, a, i){
26342                 i += 1;
26343                 if((i%2) == 0){
26344                     m.dom.xmonth = 5 + Math.round(i * .5);
26345                 }else{
26346                     m.dom.xmonth = Math.round((i-1) * .5);
26347                 }
26348             });
26349         }
26350     },
26351
26352     showMonthPicker : function(){
26353         this.createMonthPicker();
26354         var size = this.el.getSize();
26355         this.monthPicker.setSize(size);
26356         this.monthPicker.child('table').setSize(size);
26357
26358         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26359         this.updateMPMonth(this.mpSelMonth);
26360         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26361         this.updateMPYear(this.mpSelYear);
26362
26363         this.monthPicker.slideIn('t', {duration:.2});
26364     },
26365
26366     updateMPYear : function(y){
26367         this.mpyear = y;
26368         var ys = this.mpYears.elements;
26369         for(var i = 1; i <= 10; i++){
26370             var td = ys[i-1], y2;
26371             if((i%2) == 0){
26372                 y2 = y + Math.round(i * .5);
26373                 td.firstChild.innerHTML = y2;
26374                 td.xyear = y2;
26375             }else{
26376                 y2 = y - (5-Math.round(i * .5));
26377                 td.firstChild.innerHTML = y2;
26378                 td.xyear = y2;
26379             }
26380             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26381         }
26382     },
26383
26384     updateMPMonth : function(sm){
26385         this.mpMonths.each(function(m, a, i){
26386             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26387         });
26388     },
26389
26390     selectMPMonth: function(m){
26391         
26392     },
26393
26394     onMonthClick : function(e, t){
26395         e.stopEvent();
26396         var el = new Roo.Element(t), pn;
26397         if(el.is('button.x-date-mp-cancel')){
26398             this.hideMonthPicker();
26399         }
26400         else if(el.is('button.x-date-mp-ok')){
26401             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26402             this.hideMonthPicker();
26403         }
26404         else if(pn = el.up('td.x-date-mp-month', 2)){
26405             this.mpMonths.removeClass('x-date-mp-sel');
26406             pn.addClass('x-date-mp-sel');
26407             this.mpSelMonth = pn.dom.xmonth;
26408         }
26409         else if(pn = el.up('td.x-date-mp-year', 2)){
26410             this.mpYears.removeClass('x-date-mp-sel');
26411             pn.addClass('x-date-mp-sel');
26412             this.mpSelYear = pn.dom.xyear;
26413         }
26414         else if(el.is('a.x-date-mp-prev')){
26415             this.updateMPYear(this.mpyear-10);
26416         }
26417         else if(el.is('a.x-date-mp-next')){
26418             this.updateMPYear(this.mpyear+10);
26419         }
26420     },
26421
26422     onMonthDblClick : function(e, t){
26423         e.stopEvent();
26424         var el = new Roo.Element(t), pn;
26425         if(pn = el.up('td.x-date-mp-month', 2)){
26426             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26427             this.hideMonthPicker();
26428         }
26429         else if(pn = el.up('td.x-date-mp-year', 2)){
26430             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26431             this.hideMonthPicker();
26432         }
26433     },
26434
26435     hideMonthPicker : function(disableAnim){
26436         if(this.monthPicker){
26437             if(disableAnim === true){
26438                 this.monthPicker.hide();
26439             }else{
26440                 this.monthPicker.slideOut('t', {duration:.2});
26441             }
26442         }
26443     },
26444
26445     // private
26446     showPrevMonth : function(e){
26447         this.update(this.activeDate.add("mo", -1));
26448     },
26449
26450     // private
26451     showNextMonth : function(e){
26452         this.update(this.activeDate.add("mo", 1));
26453     },
26454
26455     // private
26456     showPrevYear : function(){
26457         this.update(this.activeDate.add("y", -1));
26458     },
26459
26460     // private
26461     showNextYear : function(){
26462         this.update(this.activeDate.add("y", 1));
26463     },
26464
26465     // private
26466     handleMouseWheel : function(e){
26467         var delta = e.getWheelDelta();
26468         if(delta > 0){
26469             this.showPrevMonth();
26470             e.stopEvent();
26471         } else if(delta < 0){
26472             this.showNextMonth();
26473             e.stopEvent();
26474         }
26475     },
26476
26477     // private
26478     handleDateClick : function(e, t){
26479         e.stopEvent();
26480         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26481             this.setValue(new Date(t.dateValue));
26482             this.fireEvent("select", this, this.value);
26483         }
26484     },
26485
26486     // private
26487     selectToday : function(){
26488         this.setValue(new Date().clearTime());
26489         this.fireEvent("select", this, this.value);
26490     },
26491
26492     // private
26493     update : function(date)
26494     {
26495         var vd = this.activeDate;
26496         this.activeDate = date;
26497         if(vd && this.el){
26498             var t = date.getTime();
26499             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26500                 this.cells.removeClass("x-date-selected");
26501                 this.cells.each(function(c){
26502                    if(c.dom.firstChild.dateValue == t){
26503                        c.addClass("x-date-selected");
26504                        setTimeout(function(){
26505                             try{c.dom.firstChild.focus();}catch(e){}
26506                        }, 50);
26507                        return false;
26508                    }
26509                 });
26510                 return;
26511             }
26512         }
26513         
26514         var days = date.getDaysInMonth();
26515         var firstOfMonth = date.getFirstDateOfMonth();
26516         var startingPos = firstOfMonth.getDay()-this.startDay;
26517
26518         if(startingPos <= this.startDay){
26519             startingPos += 7;
26520         }
26521
26522         var pm = date.add("mo", -1);
26523         var prevStart = pm.getDaysInMonth()-startingPos;
26524
26525         var cells = this.cells.elements;
26526         var textEls = this.textNodes;
26527         days += startingPos;
26528
26529         // convert everything to numbers so it's fast
26530         var day = 86400000;
26531         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26532         var today = new Date().clearTime().getTime();
26533         var sel = date.clearTime().getTime();
26534         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26535         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26536         var ddMatch = this.disabledDatesRE;
26537         var ddText = this.disabledDatesText;
26538         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26539         var ddaysText = this.disabledDaysText;
26540         var format = this.format;
26541
26542         var setCellClass = function(cal, cell){
26543             cell.title = "";
26544             var t = d.getTime();
26545             cell.firstChild.dateValue = t;
26546             if(t == today){
26547                 cell.className += " x-date-today";
26548                 cell.title = cal.todayText;
26549             }
26550             if(t == sel){
26551                 cell.className += " x-date-selected";
26552                 setTimeout(function(){
26553                     try{cell.firstChild.focus();}catch(e){}
26554                 }, 50);
26555             }
26556             // disabling
26557             if(t < min) {
26558                 cell.className = " x-date-disabled";
26559                 cell.title = cal.minText;
26560                 return;
26561             }
26562             if(t > max) {
26563                 cell.className = " x-date-disabled";
26564                 cell.title = cal.maxText;
26565                 return;
26566             }
26567             if(ddays){
26568                 if(ddays.indexOf(d.getDay()) != -1){
26569                     cell.title = ddaysText;
26570                     cell.className = " x-date-disabled";
26571                 }
26572             }
26573             if(ddMatch && format){
26574                 var fvalue = d.dateFormat(format);
26575                 if(ddMatch.test(fvalue)){
26576                     cell.title = ddText.replace("%0", fvalue);
26577                     cell.className = " x-date-disabled";
26578                 }
26579             }
26580         };
26581
26582         var i = 0;
26583         for(; i < startingPos; i++) {
26584             textEls[i].innerHTML = (++prevStart);
26585             d.setDate(d.getDate()+1);
26586             cells[i].className = "x-date-prevday";
26587             setCellClass(this, cells[i]);
26588         }
26589         for(; i < days; i++){
26590             intDay = i - startingPos + 1;
26591             textEls[i].innerHTML = (intDay);
26592             d.setDate(d.getDate()+1);
26593             cells[i].className = "x-date-active";
26594             setCellClass(this, cells[i]);
26595         }
26596         var extraDays = 0;
26597         for(; i < 42; i++) {
26598              textEls[i].innerHTML = (++extraDays);
26599              d.setDate(d.getDate()+1);
26600              cells[i].className = "x-date-nextday";
26601              setCellClass(this, cells[i]);
26602         }
26603
26604         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26605         this.fireEvent('monthchange', this, date);
26606         
26607         if(!this.internalRender){
26608             var main = this.el.dom.firstChild;
26609             var w = main.offsetWidth;
26610             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26611             Roo.fly(main).setWidth(w);
26612             this.internalRender = true;
26613             // opera does not respect the auto grow header center column
26614             // then, after it gets a width opera refuses to recalculate
26615             // without a second pass
26616             if(Roo.isOpera && !this.secondPass){
26617                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26618                 this.secondPass = true;
26619                 this.update.defer(10, this, [date]);
26620             }
26621         }
26622         
26623         
26624     }
26625 });        /*
26626  * Based on:
26627  * Ext JS Library 1.1.1
26628  * Copyright(c) 2006-2007, Ext JS, LLC.
26629  *
26630  * Originally Released Under LGPL - original licence link has changed is not relivant.
26631  *
26632  * Fork - LGPL
26633  * <script type="text/javascript">
26634  */
26635 /**
26636  * @class Roo.TabPanel
26637  * @extends Roo.util.Observable
26638  * A lightweight tab container.
26639  * <br><br>
26640  * Usage:
26641  * <pre><code>
26642 // basic tabs 1, built from existing content
26643 var tabs = new Roo.TabPanel("tabs1");
26644 tabs.addTab("script", "View Script");
26645 tabs.addTab("markup", "View Markup");
26646 tabs.activate("script");
26647
26648 // more advanced tabs, built from javascript
26649 var jtabs = new Roo.TabPanel("jtabs");
26650 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26651
26652 // set up the UpdateManager
26653 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26654 var updater = tab2.getUpdateManager();
26655 updater.setDefaultUrl("ajax1.htm");
26656 tab2.on('activate', updater.refresh, updater, true);
26657
26658 // Use setUrl for Ajax loading
26659 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26660 tab3.setUrl("ajax2.htm", null, true);
26661
26662 // Disabled tab
26663 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26664 tab4.disable();
26665
26666 jtabs.activate("jtabs-1");
26667  * </code></pre>
26668  * @constructor
26669  * Create a new TabPanel.
26670  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26671  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26672  */
26673 Roo.TabPanel = function(container, config){
26674     /**
26675     * The container element for this TabPanel.
26676     * @type Roo.Element
26677     */
26678     this.el = Roo.get(container, true);
26679     if(config){
26680         if(typeof config == "boolean"){
26681             this.tabPosition = config ? "bottom" : "top";
26682         }else{
26683             Roo.apply(this, config);
26684         }
26685     }
26686     if(this.tabPosition == "bottom"){
26687         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26688         this.el.addClass("x-tabs-bottom");
26689     }
26690     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26691     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26692     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26693     if(Roo.isIE){
26694         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26695     }
26696     if(this.tabPosition != "bottom"){
26697         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26698          * @type Roo.Element
26699          */
26700         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26701         this.el.addClass("x-tabs-top");
26702     }
26703     this.items = [];
26704
26705     this.bodyEl.setStyle("position", "relative");
26706
26707     this.active = null;
26708     this.activateDelegate = this.activate.createDelegate(this);
26709
26710     this.addEvents({
26711         /**
26712          * @event tabchange
26713          * Fires when the active tab changes
26714          * @param {Roo.TabPanel} this
26715          * @param {Roo.TabPanelItem} activePanel The new active tab
26716          */
26717         "tabchange": true,
26718         /**
26719          * @event beforetabchange
26720          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26721          * @param {Roo.TabPanel} this
26722          * @param {Object} e Set cancel to true on this object to cancel the tab change
26723          * @param {Roo.TabPanelItem} tab The tab being changed to
26724          */
26725         "beforetabchange" : true
26726     });
26727
26728     Roo.EventManager.onWindowResize(this.onResize, this);
26729     this.cpad = this.el.getPadding("lr");
26730     this.hiddenCount = 0;
26731
26732
26733     // toolbar on the tabbar support...
26734     if (this.toolbar) {
26735         var tcfg = this.toolbar;
26736         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26737         this.toolbar = new Roo.Toolbar(tcfg);
26738         if (Roo.isSafari) {
26739             var tbl = tcfg.container.child('table', true);
26740             tbl.setAttribute('width', '100%');
26741         }
26742         
26743     }
26744    
26745
26746
26747     Roo.TabPanel.superclass.constructor.call(this);
26748 };
26749
26750 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26751     /*
26752      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26753      */
26754     tabPosition : "top",
26755     /*
26756      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26757      */
26758     currentTabWidth : 0,
26759     /*
26760      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26761      */
26762     minTabWidth : 40,
26763     /*
26764      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26765      */
26766     maxTabWidth : 250,
26767     /*
26768      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26769      */
26770     preferredTabWidth : 175,
26771     /*
26772      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26773      */
26774     resizeTabs : false,
26775     /*
26776      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26777      */
26778     monitorResize : true,
26779     /*
26780      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26781      */
26782     toolbar : false,
26783
26784     /**
26785      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26786      * @param {String} id The id of the div to use <b>or create</b>
26787      * @param {String} text The text for the tab
26788      * @param {String} content (optional) Content to put in the TabPanelItem body
26789      * @param {Boolean} closable (optional) True to create a close icon on the tab
26790      * @return {Roo.TabPanelItem} The created TabPanelItem
26791      */
26792     addTab : function(id, text, content, closable){
26793         var item = new Roo.TabPanelItem(this, id, text, closable);
26794         this.addTabItem(item);
26795         if(content){
26796             item.setContent(content);
26797         }
26798         return item;
26799     },
26800
26801     /**
26802      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26803      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26804      * @return {Roo.TabPanelItem}
26805      */
26806     getTab : function(id){
26807         return this.items[id];
26808     },
26809
26810     /**
26811      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26812      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26813      */
26814     hideTab : function(id){
26815         var t = this.items[id];
26816         if(!t.isHidden()){
26817            t.setHidden(true);
26818            this.hiddenCount++;
26819            this.autoSizeTabs();
26820         }
26821     },
26822
26823     /**
26824      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26825      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26826      */
26827     unhideTab : function(id){
26828         var t = this.items[id];
26829         if(t.isHidden()){
26830            t.setHidden(false);
26831            this.hiddenCount--;
26832            this.autoSizeTabs();
26833         }
26834     },
26835
26836     /**
26837      * Adds an existing {@link Roo.TabPanelItem}.
26838      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26839      */
26840     addTabItem : function(item){
26841         this.items[item.id] = item;
26842         this.items.push(item);
26843         if(this.resizeTabs){
26844            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26845            this.autoSizeTabs();
26846         }else{
26847             item.autoSize();
26848         }
26849     },
26850
26851     /**
26852      * Removes a {@link Roo.TabPanelItem}.
26853      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26854      */
26855     removeTab : function(id){
26856         var items = this.items;
26857         var tab = items[id];
26858         if(!tab) { return; }
26859         var index = items.indexOf(tab);
26860         if(this.active == tab && items.length > 1){
26861             var newTab = this.getNextAvailable(index);
26862             if(newTab) {
26863                 newTab.activate();
26864             }
26865         }
26866         this.stripEl.dom.removeChild(tab.pnode.dom);
26867         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26868             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26869         }
26870         items.splice(index, 1);
26871         delete this.items[tab.id];
26872         tab.fireEvent("close", tab);
26873         tab.purgeListeners();
26874         this.autoSizeTabs();
26875     },
26876
26877     getNextAvailable : function(start){
26878         var items = this.items;
26879         var index = start;
26880         // look for a next tab that will slide over to
26881         // replace the one being removed
26882         while(index < items.length){
26883             var item = items[++index];
26884             if(item && !item.isHidden()){
26885                 return item;
26886             }
26887         }
26888         // if one isn't found select the previous tab (on the left)
26889         index = start;
26890         while(index >= 0){
26891             var item = items[--index];
26892             if(item && !item.isHidden()){
26893                 return item;
26894             }
26895         }
26896         return null;
26897     },
26898
26899     /**
26900      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26901      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26902      */
26903     disableTab : function(id){
26904         var tab = this.items[id];
26905         if(tab && this.active != tab){
26906             tab.disable();
26907         }
26908     },
26909
26910     /**
26911      * Enables a {@link Roo.TabPanelItem} that is disabled.
26912      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26913      */
26914     enableTab : function(id){
26915         var tab = this.items[id];
26916         tab.enable();
26917     },
26918
26919     /**
26920      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26921      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26922      * @return {Roo.TabPanelItem} The TabPanelItem.
26923      */
26924     activate : function(id){
26925         var tab = this.items[id];
26926         if(!tab){
26927             return null;
26928         }
26929         if(tab == this.active || tab.disabled){
26930             return tab;
26931         }
26932         var e = {};
26933         this.fireEvent("beforetabchange", this, e, tab);
26934         if(e.cancel !== true && !tab.disabled){
26935             if(this.active){
26936                 this.active.hide();
26937             }
26938             this.active = this.items[id];
26939             this.active.show();
26940             this.fireEvent("tabchange", this, this.active);
26941         }
26942         return tab;
26943     },
26944
26945     /**
26946      * Gets the active {@link Roo.TabPanelItem}.
26947      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26948      */
26949     getActiveTab : function(){
26950         return this.active;
26951     },
26952
26953     /**
26954      * Updates the tab body element to fit the height of the container element
26955      * for overflow scrolling
26956      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26957      */
26958     syncHeight : function(targetHeight){
26959         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26960         var bm = this.bodyEl.getMargins();
26961         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26962         this.bodyEl.setHeight(newHeight);
26963         return newHeight;
26964     },
26965
26966     onResize : function(){
26967         if(this.monitorResize){
26968             this.autoSizeTabs();
26969         }
26970     },
26971
26972     /**
26973      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26974      */
26975     beginUpdate : function(){
26976         this.updating = true;
26977     },
26978
26979     /**
26980      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26981      */
26982     endUpdate : function(){
26983         this.updating = false;
26984         this.autoSizeTabs();
26985     },
26986
26987     /**
26988      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26989      */
26990     autoSizeTabs : function(){
26991         var count = this.items.length;
26992         var vcount = count - this.hiddenCount;
26993         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26994         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26995         var availWidth = Math.floor(w / vcount);
26996         var b = this.stripBody;
26997         if(b.getWidth() > w){
26998             var tabs = this.items;
26999             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27000             if(availWidth < this.minTabWidth){
27001                 /*if(!this.sleft){    // incomplete scrolling code
27002                     this.createScrollButtons();
27003                 }
27004                 this.showScroll();
27005                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27006             }
27007         }else{
27008             if(this.currentTabWidth < this.preferredTabWidth){
27009                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27010             }
27011         }
27012     },
27013
27014     /**
27015      * Returns the number of tabs in this TabPanel.
27016      * @return {Number}
27017      */
27018      getCount : function(){
27019          return this.items.length;
27020      },
27021
27022     /**
27023      * Resizes all the tabs to the passed width
27024      * @param {Number} The new width
27025      */
27026     setTabWidth : function(width){
27027         this.currentTabWidth = width;
27028         for(var i = 0, len = this.items.length; i < len; i++) {
27029                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27030         }
27031     },
27032
27033     /**
27034      * Destroys this TabPanel
27035      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27036      */
27037     destroy : function(removeEl){
27038         Roo.EventManager.removeResizeListener(this.onResize, this);
27039         for(var i = 0, len = this.items.length; i < len; i++){
27040             this.items[i].purgeListeners();
27041         }
27042         if(removeEl === true){
27043             this.el.update("");
27044             this.el.remove();
27045         }
27046     }
27047 });
27048
27049 /**
27050  * @class Roo.TabPanelItem
27051  * @extends Roo.util.Observable
27052  * Represents an individual item (tab plus body) in a TabPanel.
27053  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27054  * @param {String} id The id of this TabPanelItem
27055  * @param {String} text The text for the tab of this TabPanelItem
27056  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27057  */
27058 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27059     /**
27060      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27061      * @type Roo.TabPanel
27062      */
27063     this.tabPanel = tabPanel;
27064     /**
27065      * The id for this TabPanelItem
27066      * @type String
27067      */
27068     this.id = id;
27069     /** @private */
27070     this.disabled = false;
27071     /** @private */
27072     this.text = text;
27073     /** @private */
27074     this.loaded = false;
27075     this.closable = closable;
27076
27077     /**
27078      * The body element for this TabPanelItem.
27079      * @type Roo.Element
27080      */
27081     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27082     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27083     this.bodyEl.setStyle("display", "block");
27084     this.bodyEl.setStyle("zoom", "1");
27085     this.hideAction();
27086
27087     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27088     /** @private */
27089     this.el = Roo.get(els.el, true);
27090     this.inner = Roo.get(els.inner, true);
27091     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27092     this.pnode = Roo.get(els.el.parentNode, true);
27093     this.el.on("mousedown", this.onTabMouseDown, this);
27094     this.el.on("click", this.onTabClick, this);
27095     /** @private */
27096     if(closable){
27097         var c = Roo.get(els.close, true);
27098         c.dom.title = this.closeText;
27099         c.addClassOnOver("close-over");
27100         c.on("click", this.closeClick, this);
27101      }
27102
27103     this.addEvents({
27104          /**
27105          * @event activate
27106          * Fires when this tab becomes the active tab.
27107          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27108          * @param {Roo.TabPanelItem} this
27109          */
27110         "activate": true,
27111         /**
27112          * @event beforeclose
27113          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27114          * @param {Roo.TabPanelItem} this
27115          * @param {Object} e Set cancel to true on this object to cancel the close.
27116          */
27117         "beforeclose": true,
27118         /**
27119          * @event close
27120          * Fires when this tab is closed.
27121          * @param {Roo.TabPanelItem} this
27122          */
27123          "close": true,
27124         /**
27125          * @event deactivate
27126          * Fires when this tab is no longer the active tab.
27127          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27128          * @param {Roo.TabPanelItem} this
27129          */
27130          "deactivate" : true
27131     });
27132     this.hidden = false;
27133
27134     Roo.TabPanelItem.superclass.constructor.call(this);
27135 };
27136
27137 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27138     purgeListeners : function(){
27139        Roo.util.Observable.prototype.purgeListeners.call(this);
27140        this.el.removeAllListeners();
27141     },
27142     /**
27143      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27144      */
27145     show : function(){
27146         this.pnode.addClass("on");
27147         this.showAction();
27148         if(Roo.isOpera){
27149             this.tabPanel.stripWrap.repaint();
27150         }
27151         this.fireEvent("activate", this.tabPanel, this);
27152     },
27153
27154     /**
27155      * Returns true if this tab is the active tab.
27156      * @return {Boolean}
27157      */
27158     isActive : function(){
27159         return this.tabPanel.getActiveTab() == this;
27160     },
27161
27162     /**
27163      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27164      */
27165     hide : function(){
27166         this.pnode.removeClass("on");
27167         this.hideAction();
27168         this.fireEvent("deactivate", this.tabPanel, this);
27169     },
27170
27171     hideAction : function(){
27172         this.bodyEl.hide();
27173         this.bodyEl.setStyle("position", "absolute");
27174         this.bodyEl.setLeft("-20000px");
27175         this.bodyEl.setTop("-20000px");
27176     },
27177
27178     showAction : function(){
27179         this.bodyEl.setStyle("position", "relative");
27180         this.bodyEl.setTop("");
27181         this.bodyEl.setLeft("");
27182         this.bodyEl.show();
27183     },
27184
27185     /**
27186      * Set the tooltip for the tab.
27187      * @param {String} tooltip The tab's tooltip
27188      */
27189     setTooltip : function(text){
27190         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27191             this.textEl.dom.qtip = text;
27192             this.textEl.dom.removeAttribute('title');
27193         }else{
27194             this.textEl.dom.title = text;
27195         }
27196     },
27197
27198     onTabClick : function(e){
27199         e.preventDefault();
27200         this.tabPanel.activate(this.id);
27201     },
27202
27203     onTabMouseDown : function(e){
27204         e.preventDefault();
27205         this.tabPanel.activate(this.id);
27206     },
27207
27208     getWidth : function(){
27209         return this.inner.getWidth();
27210     },
27211
27212     setWidth : function(width){
27213         var iwidth = width - this.pnode.getPadding("lr");
27214         this.inner.setWidth(iwidth);
27215         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27216         this.pnode.setWidth(width);
27217     },
27218
27219     /**
27220      * Show or hide the tab
27221      * @param {Boolean} hidden True to hide or false to show.
27222      */
27223     setHidden : function(hidden){
27224         this.hidden = hidden;
27225         this.pnode.setStyle("display", hidden ? "none" : "");
27226     },
27227
27228     /**
27229      * Returns true if this tab is "hidden"
27230      * @return {Boolean}
27231      */
27232     isHidden : function(){
27233         return this.hidden;
27234     },
27235
27236     /**
27237      * Returns the text for this tab
27238      * @return {String}
27239      */
27240     getText : function(){
27241         return this.text;
27242     },
27243
27244     autoSize : function(){
27245         //this.el.beginMeasure();
27246         this.textEl.setWidth(1);
27247         /*
27248          *  #2804 [new] Tabs in Roojs
27249          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27250          */
27251         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27252         //this.el.endMeasure();
27253     },
27254
27255     /**
27256      * Sets the text for the tab (Note: this also sets the tooltip text)
27257      * @param {String} text The tab's text and tooltip
27258      */
27259     setText : function(text){
27260         this.text = text;
27261         this.textEl.update(text);
27262         this.setTooltip(text);
27263         if(!this.tabPanel.resizeTabs){
27264             this.autoSize();
27265         }
27266     },
27267     /**
27268      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27269      */
27270     activate : function(){
27271         this.tabPanel.activate(this.id);
27272     },
27273
27274     /**
27275      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27276      */
27277     disable : function(){
27278         if(this.tabPanel.active != this){
27279             this.disabled = true;
27280             this.pnode.addClass("disabled");
27281         }
27282     },
27283
27284     /**
27285      * Enables this TabPanelItem if it was previously disabled.
27286      */
27287     enable : function(){
27288         this.disabled = false;
27289         this.pnode.removeClass("disabled");
27290     },
27291
27292     /**
27293      * Sets the content for this TabPanelItem.
27294      * @param {String} content The content
27295      * @param {Boolean} loadScripts true to look for and load scripts
27296      */
27297     setContent : function(content, loadScripts){
27298         this.bodyEl.update(content, loadScripts);
27299     },
27300
27301     /**
27302      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27303      * @return {Roo.UpdateManager} The UpdateManager
27304      */
27305     getUpdateManager : function(){
27306         return this.bodyEl.getUpdateManager();
27307     },
27308
27309     /**
27310      * Set a URL to be used to load the content for this TabPanelItem.
27311      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27312      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27313      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27314      * @return {Roo.UpdateManager} The UpdateManager
27315      */
27316     setUrl : function(url, params, loadOnce){
27317         if(this.refreshDelegate){
27318             this.un('activate', this.refreshDelegate);
27319         }
27320         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27321         this.on("activate", this.refreshDelegate);
27322         return this.bodyEl.getUpdateManager();
27323     },
27324
27325     /** @private */
27326     _handleRefresh : function(url, params, loadOnce){
27327         if(!loadOnce || !this.loaded){
27328             var updater = this.bodyEl.getUpdateManager();
27329             updater.update(url, params, this._setLoaded.createDelegate(this));
27330         }
27331     },
27332
27333     /**
27334      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27335      *   Will fail silently if the setUrl method has not been called.
27336      *   This does not activate the panel, just updates its content.
27337      */
27338     refresh : function(){
27339         if(this.refreshDelegate){
27340            this.loaded = false;
27341            this.refreshDelegate();
27342         }
27343     },
27344
27345     /** @private */
27346     _setLoaded : function(){
27347         this.loaded = true;
27348     },
27349
27350     /** @private */
27351     closeClick : function(e){
27352         var o = {};
27353         e.stopEvent();
27354         this.fireEvent("beforeclose", this, o);
27355         if(o.cancel !== true){
27356             this.tabPanel.removeTab(this.id);
27357         }
27358     },
27359     /**
27360      * The text displayed in the tooltip for the close icon.
27361      * @type String
27362      */
27363     closeText : "Close this tab"
27364 });
27365
27366 /** @private */
27367 Roo.TabPanel.prototype.createStrip = function(container){
27368     var strip = document.createElement("div");
27369     strip.className = "x-tabs-wrap";
27370     container.appendChild(strip);
27371     return strip;
27372 };
27373 /** @private */
27374 Roo.TabPanel.prototype.createStripList = function(strip){
27375     // div wrapper for retard IE
27376     // returns the "tr" element.
27377     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27378         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27379         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27380     return strip.firstChild.firstChild.firstChild.firstChild;
27381 };
27382 /** @private */
27383 Roo.TabPanel.prototype.createBody = function(container){
27384     var body = document.createElement("div");
27385     Roo.id(body, "tab-body");
27386     Roo.fly(body).addClass("x-tabs-body");
27387     container.appendChild(body);
27388     return body;
27389 };
27390 /** @private */
27391 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27392     var body = Roo.getDom(id);
27393     if(!body){
27394         body = document.createElement("div");
27395         body.id = id;
27396     }
27397     Roo.fly(body).addClass("x-tabs-item-body");
27398     bodyEl.insertBefore(body, bodyEl.firstChild);
27399     return body;
27400 };
27401 /** @private */
27402 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27403     var td = document.createElement("td");
27404     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27405     //stripEl.appendChild(td);
27406     if(closable){
27407         td.className = "x-tabs-closable";
27408         if(!this.closeTpl){
27409             this.closeTpl = new Roo.Template(
27410                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27411                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27412                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27413             );
27414         }
27415         var el = this.closeTpl.overwrite(td, {"text": text});
27416         var close = el.getElementsByTagName("div")[0];
27417         var inner = el.getElementsByTagName("em")[0];
27418         return {"el": el, "close": close, "inner": inner};
27419     } else {
27420         if(!this.tabTpl){
27421             this.tabTpl = new Roo.Template(
27422                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27423                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27424             );
27425         }
27426         var el = this.tabTpl.overwrite(td, {"text": text});
27427         var inner = el.getElementsByTagName("em")[0];
27428         return {"el": el, "inner": inner};
27429     }
27430 };/*
27431  * Based on:
27432  * Ext JS Library 1.1.1
27433  * Copyright(c) 2006-2007, Ext JS, LLC.
27434  *
27435  * Originally Released Under LGPL - original licence link has changed is not relivant.
27436  *
27437  * Fork - LGPL
27438  * <script type="text/javascript">
27439  */
27440
27441 /**
27442  * @class Roo.Button
27443  * @extends Roo.util.Observable
27444  * Simple Button class
27445  * @cfg {String} text The button text
27446  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27447  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27448  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27449  * @cfg {Object} scope The scope of the handler
27450  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27451  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27452  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27453  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27454  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27455  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27456    applies if enableToggle = true)
27457  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27458  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27459   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27460  * @constructor
27461  * Create a new button
27462  * @param {Object} config The config object
27463  */
27464 Roo.Button = function(renderTo, config)
27465 {
27466     if (!config) {
27467         config = renderTo;
27468         renderTo = config.renderTo || false;
27469     }
27470     
27471     Roo.apply(this, config);
27472     this.addEvents({
27473         /**
27474              * @event click
27475              * Fires when this button is clicked
27476              * @param {Button} this
27477              * @param {EventObject} e The click event
27478              */
27479             "click" : true,
27480         /**
27481              * @event toggle
27482              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27483              * @param {Button} this
27484              * @param {Boolean} pressed
27485              */
27486             "toggle" : true,
27487         /**
27488              * @event mouseover
27489              * Fires when the mouse hovers over the button
27490              * @param {Button} this
27491              * @param {Event} e The event object
27492              */
27493         'mouseover' : true,
27494         /**
27495              * @event mouseout
27496              * Fires when the mouse exits the button
27497              * @param {Button} this
27498              * @param {Event} e The event object
27499              */
27500         'mouseout': true,
27501          /**
27502              * @event render
27503              * Fires when the button is rendered
27504              * @param {Button} this
27505              */
27506         'render': true
27507     });
27508     if(this.menu){
27509         this.menu = Roo.menu.MenuMgr.get(this.menu);
27510     }
27511     // register listeners first!!  - so render can be captured..
27512     Roo.util.Observable.call(this);
27513     if(renderTo){
27514         this.render(renderTo);
27515     }
27516     
27517   
27518 };
27519
27520 Roo.extend(Roo.Button, Roo.util.Observable, {
27521     /**
27522      * 
27523      */
27524     
27525     /**
27526      * Read-only. True if this button is hidden
27527      * @type Boolean
27528      */
27529     hidden : false,
27530     /**
27531      * Read-only. True if this button is disabled
27532      * @type Boolean
27533      */
27534     disabled : false,
27535     /**
27536      * Read-only. True if this button is pressed (only if enableToggle = true)
27537      * @type Boolean
27538      */
27539     pressed : false,
27540
27541     /**
27542      * @cfg {Number} tabIndex 
27543      * The DOM tabIndex for this button (defaults to undefined)
27544      */
27545     tabIndex : undefined,
27546
27547     /**
27548      * @cfg {Boolean} enableToggle
27549      * True to enable pressed/not pressed toggling (defaults to false)
27550      */
27551     enableToggle: false,
27552     /**
27553      * @cfg {Mixed} menu
27554      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27555      */
27556     menu : undefined,
27557     /**
27558      * @cfg {String} menuAlign
27559      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27560      */
27561     menuAlign : "tl-bl?",
27562
27563     /**
27564      * @cfg {String} iconCls
27565      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27566      */
27567     iconCls : undefined,
27568     /**
27569      * @cfg {String} type
27570      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27571      */
27572     type : 'button',
27573
27574     // private
27575     menuClassTarget: 'tr',
27576
27577     /**
27578      * @cfg {String} clickEvent
27579      * The type of event to map to the button's event handler (defaults to 'click')
27580      */
27581     clickEvent : 'click',
27582
27583     /**
27584      * @cfg {Boolean} handleMouseEvents
27585      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27586      */
27587     handleMouseEvents : true,
27588
27589     /**
27590      * @cfg {String} tooltipType
27591      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27592      */
27593     tooltipType : 'qtip',
27594
27595     /**
27596      * @cfg {String} cls
27597      * A CSS class to apply to the button's main element.
27598      */
27599     
27600     /**
27601      * @cfg {Roo.Template} template (Optional)
27602      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27603      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27604      * require code modifications if required elements (e.g. a button) aren't present.
27605      */
27606
27607     // private
27608     render : function(renderTo){
27609         var btn;
27610         if(this.hideParent){
27611             this.parentEl = Roo.get(renderTo);
27612         }
27613         if(!this.dhconfig){
27614             if(!this.template){
27615                 if(!Roo.Button.buttonTemplate){
27616                     // hideous table template
27617                     Roo.Button.buttonTemplate = new Roo.Template(
27618                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27619                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27620                         "</tr></tbody></table>");
27621                 }
27622                 this.template = Roo.Button.buttonTemplate;
27623             }
27624             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27625             var btnEl = btn.child("button:first");
27626             btnEl.on('focus', this.onFocus, this);
27627             btnEl.on('blur', this.onBlur, this);
27628             if(this.cls){
27629                 btn.addClass(this.cls);
27630             }
27631             if(this.icon){
27632                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27633             }
27634             if(this.iconCls){
27635                 btnEl.addClass(this.iconCls);
27636                 if(!this.cls){
27637                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27638                 }
27639             }
27640             if(this.tabIndex !== undefined){
27641                 btnEl.dom.tabIndex = this.tabIndex;
27642             }
27643             if(this.tooltip){
27644                 if(typeof this.tooltip == 'object'){
27645                     Roo.QuickTips.tips(Roo.apply({
27646                           target: btnEl.id
27647                     }, this.tooltip));
27648                 } else {
27649                     btnEl.dom[this.tooltipType] = this.tooltip;
27650                 }
27651             }
27652         }else{
27653             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27654         }
27655         this.el = btn;
27656         if(this.id){
27657             this.el.dom.id = this.el.id = this.id;
27658         }
27659         if(this.menu){
27660             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27661             this.menu.on("show", this.onMenuShow, this);
27662             this.menu.on("hide", this.onMenuHide, this);
27663         }
27664         btn.addClass("x-btn");
27665         if(Roo.isIE && !Roo.isIE7){
27666             this.autoWidth.defer(1, this);
27667         }else{
27668             this.autoWidth();
27669         }
27670         if(this.handleMouseEvents){
27671             btn.on("mouseover", this.onMouseOver, this);
27672             btn.on("mouseout", this.onMouseOut, this);
27673             btn.on("mousedown", this.onMouseDown, this);
27674         }
27675         btn.on(this.clickEvent, this.onClick, this);
27676         //btn.on("mouseup", this.onMouseUp, this);
27677         if(this.hidden){
27678             this.hide();
27679         }
27680         if(this.disabled){
27681             this.disable();
27682         }
27683         Roo.ButtonToggleMgr.register(this);
27684         if(this.pressed){
27685             this.el.addClass("x-btn-pressed");
27686         }
27687         if(this.repeat){
27688             var repeater = new Roo.util.ClickRepeater(btn,
27689                 typeof this.repeat == "object" ? this.repeat : {}
27690             );
27691             repeater.on("click", this.onClick,  this);
27692         }
27693         
27694         this.fireEvent('render', this);
27695         
27696     },
27697     /**
27698      * Returns the button's underlying element
27699      * @return {Roo.Element} The element
27700      */
27701     getEl : function(){
27702         return this.el;  
27703     },
27704     
27705     /**
27706      * Destroys this Button and removes any listeners.
27707      */
27708     destroy : function(){
27709         Roo.ButtonToggleMgr.unregister(this);
27710         this.el.removeAllListeners();
27711         this.purgeListeners();
27712         this.el.remove();
27713     },
27714
27715     // private
27716     autoWidth : function(){
27717         if(this.el){
27718             this.el.setWidth("auto");
27719             if(Roo.isIE7 && Roo.isStrict){
27720                 var ib = this.el.child('button');
27721                 if(ib && ib.getWidth() > 20){
27722                     ib.clip();
27723                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27724                 }
27725             }
27726             if(this.minWidth){
27727                 if(this.hidden){
27728                     this.el.beginMeasure();
27729                 }
27730                 if(this.el.getWidth() < this.minWidth){
27731                     this.el.setWidth(this.minWidth);
27732                 }
27733                 if(this.hidden){
27734                     this.el.endMeasure();
27735                 }
27736             }
27737         }
27738     },
27739
27740     /**
27741      * Assigns this button's click handler
27742      * @param {Function} handler The function to call when the button is clicked
27743      * @param {Object} scope (optional) Scope for the function passed in
27744      */
27745     setHandler : function(handler, scope){
27746         this.handler = handler;
27747         this.scope = scope;  
27748     },
27749     
27750     /**
27751      * Sets this button's text
27752      * @param {String} text The button text
27753      */
27754     setText : function(text){
27755         this.text = text;
27756         if(this.el){
27757             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27758         }
27759         this.autoWidth();
27760     },
27761     
27762     /**
27763      * Gets the text for this button
27764      * @return {String} The button text
27765      */
27766     getText : function(){
27767         return this.text;  
27768     },
27769     
27770     /**
27771      * Show this button
27772      */
27773     show: function(){
27774         this.hidden = false;
27775         if(this.el){
27776             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27777         }
27778     },
27779     
27780     /**
27781      * Hide this button
27782      */
27783     hide: function(){
27784         this.hidden = true;
27785         if(this.el){
27786             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27787         }
27788     },
27789     
27790     /**
27791      * Convenience function for boolean show/hide
27792      * @param {Boolean} visible True to show, false to hide
27793      */
27794     setVisible: function(visible){
27795         if(visible) {
27796             this.show();
27797         }else{
27798             this.hide();
27799         }
27800     },
27801     
27802     /**
27803      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27804      * @param {Boolean} state (optional) Force a particular state
27805      */
27806     toggle : function(state){
27807         state = state === undefined ? !this.pressed : state;
27808         if(state != this.pressed){
27809             if(state){
27810                 this.el.addClass("x-btn-pressed");
27811                 this.pressed = true;
27812                 this.fireEvent("toggle", this, true);
27813             }else{
27814                 this.el.removeClass("x-btn-pressed");
27815                 this.pressed = false;
27816                 this.fireEvent("toggle", this, false);
27817             }
27818             if(this.toggleHandler){
27819                 this.toggleHandler.call(this.scope || this, this, state);
27820             }
27821         }
27822     },
27823     
27824     /**
27825      * Focus the button
27826      */
27827     focus : function(){
27828         this.el.child('button:first').focus();
27829     },
27830     
27831     /**
27832      * Disable this button
27833      */
27834     disable : function(){
27835         if(this.el){
27836             this.el.addClass("x-btn-disabled");
27837         }
27838         this.disabled = true;
27839     },
27840     
27841     /**
27842      * Enable this button
27843      */
27844     enable : function(){
27845         if(this.el){
27846             this.el.removeClass("x-btn-disabled");
27847         }
27848         this.disabled = false;
27849     },
27850
27851     /**
27852      * Convenience function for boolean enable/disable
27853      * @param {Boolean} enabled True to enable, false to disable
27854      */
27855     setDisabled : function(v){
27856         this[v !== true ? "enable" : "disable"]();
27857     },
27858
27859     // private
27860     onClick : function(e){
27861         if(e){
27862             e.preventDefault();
27863         }
27864         if(e.button != 0){
27865             return;
27866         }
27867         if(!this.disabled){
27868             if(this.enableToggle){
27869                 this.toggle();
27870             }
27871             if(this.menu && !this.menu.isVisible()){
27872                 this.menu.show(this.el, this.menuAlign);
27873             }
27874             this.fireEvent("click", this, e);
27875             if(this.handler){
27876                 this.el.removeClass("x-btn-over");
27877                 this.handler.call(this.scope || this, this, e);
27878             }
27879         }
27880     },
27881     // private
27882     onMouseOver : function(e){
27883         if(!this.disabled){
27884             this.el.addClass("x-btn-over");
27885             this.fireEvent('mouseover', this, e);
27886         }
27887     },
27888     // private
27889     onMouseOut : function(e){
27890         if(!e.within(this.el,  true)){
27891             this.el.removeClass("x-btn-over");
27892             this.fireEvent('mouseout', this, e);
27893         }
27894     },
27895     // private
27896     onFocus : function(e){
27897         if(!this.disabled){
27898             this.el.addClass("x-btn-focus");
27899         }
27900     },
27901     // private
27902     onBlur : function(e){
27903         this.el.removeClass("x-btn-focus");
27904     },
27905     // private
27906     onMouseDown : function(e){
27907         if(!this.disabled && e.button == 0){
27908             this.el.addClass("x-btn-click");
27909             Roo.get(document).on('mouseup', this.onMouseUp, this);
27910         }
27911     },
27912     // private
27913     onMouseUp : function(e){
27914         if(e.button == 0){
27915             this.el.removeClass("x-btn-click");
27916             Roo.get(document).un('mouseup', this.onMouseUp, this);
27917         }
27918     },
27919     // private
27920     onMenuShow : function(e){
27921         this.el.addClass("x-btn-menu-active");
27922     },
27923     // private
27924     onMenuHide : function(e){
27925         this.el.removeClass("x-btn-menu-active");
27926     }   
27927 });
27928
27929 // Private utility class used by Button
27930 Roo.ButtonToggleMgr = function(){
27931    var groups = {};
27932    
27933    function toggleGroup(btn, state){
27934        if(state){
27935            var g = groups[btn.toggleGroup];
27936            for(var i = 0, l = g.length; i < l; i++){
27937                if(g[i] != btn){
27938                    g[i].toggle(false);
27939                }
27940            }
27941        }
27942    }
27943    
27944    return {
27945        register : function(btn){
27946            if(!btn.toggleGroup){
27947                return;
27948            }
27949            var g = groups[btn.toggleGroup];
27950            if(!g){
27951                g = groups[btn.toggleGroup] = [];
27952            }
27953            g.push(btn);
27954            btn.on("toggle", toggleGroup);
27955        },
27956        
27957        unregister : function(btn){
27958            if(!btn.toggleGroup){
27959                return;
27960            }
27961            var g = groups[btn.toggleGroup];
27962            if(g){
27963                g.remove(btn);
27964                btn.un("toggle", toggleGroup);
27965            }
27966        }
27967    };
27968 }();/*
27969  * Based on:
27970  * Ext JS Library 1.1.1
27971  * Copyright(c) 2006-2007, Ext JS, LLC.
27972  *
27973  * Originally Released Under LGPL - original licence link has changed is not relivant.
27974  *
27975  * Fork - LGPL
27976  * <script type="text/javascript">
27977  */
27978  
27979 /**
27980  * @class Roo.SplitButton
27981  * @extends Roo.Button
27982  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27983  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27984  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27985  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27986  * @cfg {String} arrowTooltip The title attribute of the arrow
27987  * @constructor
27988  * Create a new menu button
27989  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27990  * @param {Object} config The config object
27991  */
27992 Roo.SplitButton = function(renderTo, config){
27993     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27994     /**
27995      * @event arrowclick
27996      * Fires when this button's arrow is clicked
27997      * @param {SplitButton} this
27998      * @param {EventObject} e The click event
27999      */
28000     this.addEvents({"arrowclick":true});
28001 };
28002
28003 Roo.extend(Roo.SplitButton, Roo.Button, {
28004     render : function(renderTo){
28005         // this is one sweet looking template!
28006         var tpl = new Roo.Template(
28007             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28008             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28009             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
28010             "</tbody></table></td><td>",
28011             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28012             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
28013             "</tbody></table></td></tr></table>"
28014         );
28015         var btn = tpl.append(renderTo, [this.text, this.type], true);
28016         var btnEl = btn.child("button");
28017         if(this.cls){
28018             btn.addClass(this.cls);
28019         }
28020         if(this.icon){
28021             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28022         }
28023         if(this.iconCls){
28024             btnEl.addClass(this.iconCls);
28025             if(!this.cls){
28026                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28027             }
28028         }
28029         this.el = btn;
28030         if(this.handleMouseEvents){
28031             btn.on("mouseover", this.onMouseOver, this);
28032             btn.on("mouseout", this.onMouseOut, this);
28033             btn.on("mousedown", this.onMouseDown, this);
28034             btn.on("mouseup", this.onMouseUp, this);
28035         }
28036         btn.on(this.clickEvent, this.onClick, this);
28037         if(this.tooltip){
28038             if(typeof this.tooltip == 'object'){
28039                 Roo.QuickTips.tips(Roo.apply({
28040                       target: btnEl.id
28041                 }, this.tooltip));
28042             } else {
28043                 btnEl.dom[this.tooltipType] = this.tooltip;
28044             }
28045         }
28046         if(this.arrowTooltip){
28047             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28048         }
28049         if(this.hidden){
28050             this.hide();
28051         }
28052         if(this.disabled){
28053             this.disable();
28054         }
28055         if(this.pressed){
28056             this.el.addClass("x-btn-pressed");
28057         }
28058         if(Roo.isIE && !Roo.isIE7){
28059             this.autoWidth.defer(1, this);
28060         }else{
28061             this.autoWidth();
28062         }
28063         if(this.menu){
28064             this.menu.on("show", this.onMenuShow, this);
28065             this.menu.on("hide", this.onMenuHide, this);
28066         }
28067         this.fireEvent('render', this);
28068     },
28069
28070     // private
28071     autoWidth : function(){
28072         if(this.el){
28073             var tbl = this.el.child("table:first");
28074             var tbl2 = this.el.child("table:last");
28075             this.el.setWidth("auto");
28076             tbl.setWidth("auto");
28077             if(Roo.isIE7 && Roo.isStrict){
28078                 var ib = this.el.child('button:first');
28079                 if(ib && ib.getWidth() > 20){
28080                     ib.clip();
28081                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28082                 }
28083             }
28084             if(this.minWidth){
28085                 if(this.hidden){
28086                     this.el.beginMeasure();
28087                 }
28088                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28089                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28090                 }
28091                 if(this.hidden){
28092                     this.el.endMeasure();
28093                 }
28094             }
28095             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28096         } 
28097     },
28098     /**
28099      * Sets this button's click handler
28100      * @param {Function} handler The function to call when the button is clicked
28101      * @param {Object} scope (optional) Scope for the function passed above
28102      */
28103     setHandler : function(handler, scope){
28104         this.handler = handler;
28105         this.scope = scope;  
28106     },
28107     
28108     /**
28109      * Sets this button's arrow click handler
28110      * @param {Function} handler The function to call when the arrow is clicked
28111      * @param {Object} scope (optional) Scope for the function passed above
28112      */
28113     setArrowHandler : function(handler, scope){
28114         this.arrowHandler = handler;
28115         this.scope = scope;  
28116     },
28117     
28118     /**
28119      * Focus the button
28120      */
28121     focus : function(){
28122         if(this.el){
28123             this.el.child("button:first").focus();
28124         }
28125     },
28126
28127     // private
28128     onClick : function(e){
28129         e.preventDefault();
28130         if(!this.disabled){
28131             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28132                 if(this.menu && !this.menu.isVisible()){
28133                     this.menu.show(this.el, this.menuAlign);
28134                 }
28135                 this.fireEvent("arrowclick", this, e);
28136                 if(this.arrowHandler){
28137                     this.arrowHandler.call(this.scope || this, this, e);
28138                 }
28139             }else{
28140                 this.fireEvent("click", this, e);
28141                 if(this.handler){
28142                     this.handler.call(this.scope || this, this, e);
28143                 }
28144             }
28145         }
28146     },
28147     // private
28148     onMouseDown : function(e){
28149         if(!this.disabled){
28150             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28151         }
28152     },
28153     // private
28154     onMouseUp : function(e){
28155         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28156     }   
28157 });
28158
28159
28160 // backwards compat
28161 Roo.MenuButton = Roo.SplitButton;/*
28162  * Based on:
28163  * Ext JS Library 1.1.1
28164  * Copyright(c) 2006-2007, Ext JS, LLC.
28165  *
28166  * Originally Released Under LGPL - original licence link has changed is not relivant.
28167  *
28168  * Fork - LGPL
28169  * <script type="text/javascript">
28170  */
28171
28172 /**
28173  * @class Roo.Toolbar
28174  * Basic Toolbar class.
28175  * @constructor
28176  * Creates a new Toolbar
28177  * @param {Object} container The config object
28178  */ 
28179 Roo.Toolbar = function(container, buttons, config)
28180 {
28181     /// old consturctor format still supported..
28182     if(container instanceof Array){ // omit the container for later rendering
28183         buttons = container;
28184         config = buttons;
28185         container = null;
28186     }
28187     if (typeof(container) == 'object' && container.xtype) {
28188         config = container;
28189         container = config.container;
28190         buttons = config.buttons || []; // not really - use items!!
28191     }
28192     var xitems = [];
28193     if (config && config.items) {
28194         xitems = config.items;
28195         delete config.items;
28196     }
28197     Roo.apply(this, config);
28198     this.buttons = buttons;
28199     
28200     if(container){
28201         this.render(container);
28202     }
28203     this.xitems = xitems;
28204     Roo.each(xitems, function(b) {
28205         this.add(b);
28206     }, this);
28207     
28208 };
28209
28210 Roo.Toolbar.prototype = {
28211     /**
28212      * @cfg {Array} items
28213      * array of button configs or elements to add (will be converted to a MixedCollection)
28214      */
28215     
28216     /**
28217      * @cfg {String/HTMLElement/Element} container
28218      * The id or element that will contain the toolbar
28219      */
28220     // private
28221     render : function(ct){
28222         this.el = Roo.get(ct);
28223         if(this.cls){
28224             this.el.addClass(this.cls);
28225         }
28226         // using a table allows for vertical alignment
28227         // 100% width is needed by Safari...
28228         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28229         this.tr = this.el.child("tr", true);
28230         var autoId = 0;
28231         this.items = new Roo.util.MixedCollection(false, function(o){
28232             return o.id || ("item" + (++autoId));
28233         });
28234         if(this.buttons){
28235             this.add.apply(this, this.buttons);
28236             delete this.buttons;
28237         }
28238     },
28239
28240     /**
28241      * Adds element(s) to the toolbar -- this function takes a variable number of 
28242      * arguments of mixed type and adds them to the toolbar.
28243      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28244      * <ul>
28245      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28246      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28247      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28248      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28249      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28250      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28251      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28252      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28253      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28254      * </ul>
28255      * @param {Mixed} arg2
28256      * @param {Mixed} etc.
28257      */
28258     add : function(){
28259         var a = arguments, l = a.length;
28260         for(var i = 0; i < l; i++){
28261             this._add(a[i]);
28262         }
28263     },
28264     // private..
28265     _add : function(el) {
28266         
28267         if (el.xtype) {
28268             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28269         }
28270         
28271         if (el.applyTo){ // some kind of form field
28272             return this.addField(el);
28273         } 
28274         if (el.render){ // some kind of Toolbar.Item
28275             return this.addItem(el);
28276         }
28277         if (typeof el == "string"){ // string
28278             if(el == "separator" || el == "-"){
28279                 return this.addSeparator();
28280             }
28281             if (el == " "){
28282                 return this.addSpacer();
28283             }
28284             if(el == "->"){
28285                 return this.addFill();
28286             }
28287             return this.addText(el);
28288             
28289         }
28290         if(el.tagName){ // element
28291             return this.addElement(el);
28292         }
28293         if(typeof el == "object"){ // must be button config?
28294             return this.addButton(el);
28295         }
28296         // and now what?!?!
28297         return false;
28298         
28299     },
28300     
28301     /**
28302      * Add an Xtype element
28303      * @param {Object} xtype Xtype Object
28304      * @return {Object} created Object
28305      */
28306     addxtype : function(e){
28307         return this.add(e);  
28308     },
28309     
28310     /**
28311      * Returns the Element for this toolbar.
28312      * @return {Roo.Element}
28313      */
28314     getEl : function(){
28315         return this.el;  
28316     },
28317     
28318     /**
28319      * Adds a separator
28320      * @return {Roo.Toolbar.Item} The separator item
28321      */
28322     addSeparator : function(){
28323         return this.addItem(new Roo.Toolbar.Separator());
28324     },
28325
28326     /**
28327      * Adds a spacer element
28328      * @return {Roo.Toolbar.Spacer} The spacer item
28329      */
28330     addSpacer : function(){
28331         return this.addItem(new Roo.Toolbar.Spacer());
28332     },
28333
28334     /**
28335      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28336      * @return {Roo.Toolbar.Fill} The fill item
28337      */
28338     addFill : function(){
28339         return this.addItem(new Roo.Toolbar.Fill());
28340     },
28341
28342     /**
28343      * Adds any standard HTML element to the toolbar
28344      * @param {String/HTMLElement/Element} el The element or id of the element to add
28345      * @return {Roo.Toolbar.Item} The element's item
28346      */
28347     addElement : function(el){
28348         return this.addItem(new Roo.Toolbar.Item(el));
28349     },
28350     /**
28351      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28352      * @type Roo.util.MixedCollection  
28353      */
28354     items : false,
28355      
28356     /**
28357      * Adds any Toolbar.Item or subclass
28358      * @param {Roo.Toolbar.Item} item
28359      * @return {Roo.Toolbar.Item} The item
28360      */
28361     addItem : function(item){
28362         var td = this.nextBlock();
28363         item.render(td);
28364         this.items.add(item);
28365         return item;
28366     },
28367     
28368     /**
28369      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28370      * @param {Object/Array} config A button config or array of configs
28371      * @return {Roo.Toolbar.Button/Array}
28372      */
28373     addButton : function(config){
28374         if(config instanceof Array){
28375             var buttons = [];
28376             for(var i = 0, len = config.length; i < len; i++) {
28377                 buttons.push(this.addButton(config[i]));
28378             }
28379             return buttons;
28380         }
28381         var b = config;
28382         if(!(config instanceof Roo.Toolbar.Button)){
28383             b = config.split ?
28384                 new Roo.Toolbar.SplitButton(config) :
28385                 new Roo.Toolbar.Button(config);
28386         }
28387         var td = this.nextBlock();
28388         b.render(td);
28389         this.items.add(b);
28390         return b;
28391     },
28392     
28393     /**
28394      * Adds text to the toolbar
28395      * @param {String} text The text to add
28396      * @return {Roo.Toolbar.Item} The element's item
28397      */
28398     addText : function(text){
28399         return this.addItem(new Roo.Toolbar.TextItem(text));
28400     },
28401     
28402     /**
28403      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28404      * @param {Number} index The index where the item is to be inserted
28405      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28406      * @return {Roo.Toolbar.Button/Item}
28407      */
28408     insertButton : function(index, item){
28409         if(item instanceof Array){
28410             var buttons = [];
28411             for(var i = 0, len = item.length; i < len; i++) {
28412                buttons.push(this.insertButton(index + i, item[i]));
28413             }
28414             return buttons;
28415         }
28416         if (!(item instanceof Roo.Toolbar.Button)){
28417            item = new Roo.Toolbar.Button(item);
28418         }
28419         var td = document.createElement("td");
28420         this.tr.insertBefore(td, this.tr.childNodes[index]);
28421         item.render(td);
28422         this.items.insert(index, item);
28423         return item;
28424     },
28425     
28426     /**
28427      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28428      * @param {Object} config
28429      * @return {Roo.Toolbar.Item} The element's item
28430      */
28431     addDom : function(config, returnEl){
28432         var td = this.nextBlock();
28433         Roo.DomHelper.overwrite(td, config);
28434         var ti = new Roo.Toolbar.Item(td.firstChild);
28435         ti.render(td);
28436         this.items.add(ti);
28437         return ti;
28438     },
28439
28440     /**
28441      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28442      * @type Roo.util.MixedCollection  
28443      */
28444     fields : false,
28445     
28446     /**
28447      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28448      * Note: the field should not have been rendered yet. For a field that has already been
28449      * rendered, use {@link #addElement}.
28450      * @param {Roo.form.Field} field
28451      * @return {Roo.ToolbarItem}
28452      */
28453      
28454       
28455     addField : function(field) {
28456         if (!this.fields) {
28457             var autoId = 0;
28458             this.fields = new Roo.util.MixedCollection(false, function(o){
28459                 return o.id || ("item" + (++autoId));
28460             });
28461
28462         }
28463         
28464         var td = this.nextBlock();
28465         field.render(td);
28466         var ti = new Roo.Toolbar.Item(td.firstChild);
28467         ti.render(td);
28468         this.items.add(ti);
28469         this.fields.add(field);
28470         return ti;
28471     },
28472     /**
28473      * Hide the toolbar
28474      * @method hide
28475      */
28476      
28477       
28478     hide : function()
28479     {
28480         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28481         this.el.child('div').hide();
28482     },
28483     /**
28484      * Show the toolbar
28485      * @method show
28486      */
28487     show : function()
28488     {
28489         this.el.child('div').show();
28490     },
28491       
28492     // private
28493     nextBlock : function(){
28494         var td = document.createElement("td");
28495         this.tr.appendChild(td);
28496         return td;
28497     },
28498
28499     // private
28500     destroy : function(){
28501         if(this.items){ // rendered?
28502             Roo.destroy.apply(Roo, this.items.items);
28503         }
28504         if(this.fields){ // rendered?
28505             Roo.destroy.apply(Roo, this.fields.items);
28506         }
28507         Roo.Element.uncache(this.el, this.tr);
28508     }
28509 };
28510
28511 /**
28512  * @class Roo.Toolbar.Item
28513  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28514  * @constructor
28515  * Creates a new Item
28516  * @param {HTMLElement} el 
28517  */
28518 Roo.Toolbar.Item = function(el){
28519     this.el = Roo.getDom(el);
28520     this.id = Roo.id(this.el);
28521     this.hidden = false;
28522 };
28523
28524 Roo.Toolbar.Item.prototype = {
28525     
28526     /**
28527      * Get this item's HTML Element
28528      * @return {HTMLElement}
28529      */
28530     getEl : function(){
28531        return this.el;  
28532     },
28533
28534     // private
28535     render : function(td){
28536         this.td = td;
28537         td.appendChild(this.el);
28538     },
28539     
28540     /**
28541      * Removes and destroys this item.
28542      */
28543     destroy : function(){
28544         this.td.parentNode.removeChild(this.td);
28545     },
28546     
28547     /**
28548      * Shows this item.
28549      */
28550     show: function(){
28551         this.hidden = false;
28552         this.td.style.display = "";
28553     },
28554     
28555     /**
28556      * Hides this item.
28557      */
28558     hide: function(){
28559         this.hidden = true;
28560         this.td.style.display = "none";
28561     },
28562     
28563     /**
28564      * Convenience function for boolean show/hide.
28565      * @param {Boolean} visible true to show/false to hide
28566      */
28567     setVisible: function(visible){
28568         if(visible) {
28569             this.show();
28570         }else{
28571             this.hide();
28572         }
28573     },
28574     
28575     /**
28576      * Try to focus this item.
28577      */
28578     focus : function(){
28579         Roo.fly(this.el).focus();
28580     },
28581     
28582     /**
28583      * Disables this item.
28584      */
28585     disable : function(){
28586         Roo.fly(this.td).addClass("x-item-disabled");
28587         this.disabled = true;
28588         this.el.disabled = true;
28589     },
28590     
28591     /**
28592      * Enables this item.
28593      */
28594     enable : function(){
28595         Roo.fly(this.td).removeClass("x-item-disabled");
28596         this.disabled = false;
28597         this.el.disabled = false;
28598     }
28599 };
28600
28601
28602 /**
28603  * @class Roo.Toolbar.Separator
28604  * @extends Roo.Toolbar.Item
28605  * A simple toolbar separator class
28606  * @constructor
28607  * Creates a new Separator
28608  */
28609 Roo.Toolbar.Separator = function(){
28610     var s = document.createElement("span");
28611     s.className = "ytb-sep";
28612     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28613 };
28614 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28615     enable:Roo.emptyFn,
28616     disable:Roo.emptyFn,
28617     focus:Roo.emptyFn
28618 });
28619
28620 /**
28621  * @class Roo.Toolbar.Spacer
28622  * @extends Roo.Toolbar.Item
28623  * A simple element that adds extra horizontal space to a toolbar.
28624  * @constructor
28625  * Creates a new Spacer
28626  */
28627 Roo.Toolbar.Spacer = function(){
28628     var s = document.createElement("div");
28629     s.className = "ytb-spacer";
28630     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28631 };
28632 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28633     enable:Roo.emptyFn,
28634     disable:Roo.emptyFn,
28635     focus:Roo.emptyFn
28636 });
28637
28638 /**
28639  * @class Roo.Toolbar.Fill
28640  * @extends Roo.Toolbar.Spacer
28641  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28642  * @constructor
28643  * Creates a new Spacer
28644  */
28645 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28646     // private
28647     render : function(td){
28648         td.style.width = '100%';
28649         Roo.Toolbar.Fill.superclass.render.call(this, td);
28650     }
28651 });
28652
28653 /**
28654  * @class Roo.Toolbar.TextItem
28655  * @extends Roo.Toolbar.Item
28656  * A simple class that renders text directly into a toolbar.
28657  * @constructor
28658  * Creates a new TextItem
28659  * @param {String} text
28660  */
28661 Roo.Toolbar.TextItem = function(text){
28662     if (typeof(text) == 'object') {
28663         text = text.text;
28664     }
28665     var s = document.createElement("span");
28666     s.className = "ytb-text";
28667     s.innerHTML = text;
28668     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28669 };
28670 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28671     enable:Roo.emptyFn,
28672     disable:Roo.emptyFn,
28673     focus:Roo.emptyFn
28674 });
28675
28676 /**
28677  * @class Roo.Toolbar.Button
28678  * @extends Roo.Button
28679  * A button that renders into a toolbar.
28680  * @constructor
28681  * Creates a new Button
28682  * @param {Object} config A standard {@link Roo.Button} config object
28683  */
28684 Roo.Toolbar.Button = function(config){
28685     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28686 };
28687 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28688     render : function(td){
28689         this.td = td;
28690         Roo.Toolbar.Button.superclass.render.call(this, td);
28691     },
28692     
28693     /**
28694      * Removes and destroys this button
28695      */
28696     destroy : function(){
28697         Roo.Toolbar.Button.superclass.destroy.call(this);
28698         this.td.parentNode.removeChild(this.td);
28699     },
28700     
28701     /**
28702      * Shows this button
28703      */
28704     show: function(){
28705         this.hidden = false;
28706         this.td.style.display = "";
28707     },
28708     
28709     /**
28710      * Hides this button
28711      */
28712     hide: function(){
28713         this.hidden = true;
28714         this.td.style.display = "none";
28715     },
28716
28717     /**
28718      * Disables this item
28719      */
28720     disable : function(){
28721         Roo.fly(this.td).addClass("x-item-disabled");
28722         this.disabled = true;
28723     },
28724
28725     /**
28726      * Enables this item
28727      */
28728     enable : function(){
28729         Roo.fly(this.td).removeClass("x-item-disabled");
28730         this.disabled = false;
28731     }
28732 });
28733 // backwards compat
28734 Roo.ToolbarButton = Roo.Toolbar.Button;
28735
28736 /**
28737  * @class Roo.Toolbar.SplitButton
28738  * @extends Roo.SplitButton
28739  * A menu button that renders into a toolbar.
28740  * @constructor
28741  * Creates a new SplitButton
28742  * @param {Object} config A standard {@link Roo.SplitButton} config object
28743  */
28744 Roo.Toolbar.SplitButton = function(config){
28745     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28746 };
28747 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28748     render : function(td){
28749         this.td = td;
28750         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28751     },
28752     
28753     /**
28754      * Removes and destroys this button
28755      */
28756     destroy : function(){
28757         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28758         this.td.parentNode.removeChild(this.td);
28759     },
28760     
28761     /**
28762      * Shows this button
28763      */
28764     show: function(){
28765         this.hidden = false;
28766         this.td.style.display = "";
28767     },
28768     
28769     /**
28770      * Hides this button
28771      */
28772     hide: function(){
28773         this.hidden = true;
28774         this.td.style.display = "none";
28775     }
28776 });
28777
28778 // backwards compat
28779 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28780  * Based on:
28781  * Ext JS Library 1.1.1
28782  * Copyright(c) 2006-2007, Ext JS, LLC.
28783  *
28784  * Originally Released Under LGPL - original licence link has changed is not relivant.
28785  *
28786  * Fork - LGPL
28787  * <script type="text/javascript">
28788  */
28789  
28790 /**
28791  * @class Roo.PagingToolbar
28792  * @extends Roo.Toolbar
28793  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28794  * @constructor
28795  * Create a new PagingToolbar
28796  * @param {Object} config The config object
28797  */
28798 Roo.PagingToolbar = function(el, ds, config)
28799 {
28800     // old args format still supported... - xtype is prefered..
28801     if (typeof(el) == 'object' && el.xtype) {
28802         // created from xtype...
28803         config = el;
28804         ds = el.dataSource;
28805         el = config.container;
28806     }
28807     var items = [];
28808     if (config.items) {
28809         items = config.items;
28810         config.items = [];
28811     }
28812     
28813     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28814     this.ds = ds;
28815     this.cursor = 0;
28816     this.renderButtons(this.el);
28817     this.bind(ds);
28818     
28819     // supprot items array.
28820    
28821     Roo.each(items, function(e) {
28822         this.add(Roo.factory(e));
28823     },this);
28824     
28825 };
28826
28827 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28828     /**
28829      * @cfg {Roo.data.Store} dataSource
28830      * The underlying data store providing the paged data
28831      */
28832     /**
28833      * @cfg {String/HTMLElement/Element} container
28834      * container The id or element that will contain the toolbar
28835      */
28836     /**
28837      * @cfg {Boolean} displayInfo
28838      * True to display the displayMsg (defaults to false)
28839      */
28840     /**
28841      * @cfg {Number} pageSize
28842      * The number of records to display per page (defaults to 20)
28843      */
28844     pageSize: 20,
28845     /**
28846      * @cfg {String} displayMsg
28847      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28848      */
28849     displayMsg : 'Displaying {0} - {1} of {2}',
28850     /**
28851      * @cfg {String} emptyMsg
28852      * The message to display when no records are found (defaults to "No data to display")
28853      */
28854     emptyMsg : 'No data to display',
28855     /**
28856      * Customizable piece of the default paging text (defaults to "Page")
28857      * @type String
28858      */
28859     beforePageText : "Page",
28860     /**
28861      * Customizable piece of the default paging text (defaults to "of %0")
28862      * @type String
28863      */
28864     afterPageText : "of {0}",
28865     /**
28866      * Customizable piece of the default paging text (defaults to "First Page")
28867      * @type String
28868      */
28869     firstText : "First Page",
28870     /**
28871      * Customizable piece of the default paging text (defaults to "Previous Page")
28872      * @type String
28873      */
28874     prevText : "Previous Page",
28875     /**
28876      * Customizable piece of the default paging text (defaults to "Next Page")
28877      * @type String
28878      */
28879     nextText : "Next Page",
28880     /**
28881      * Customizable piece of the default paging text (defaults to "Last Page")
28882      * @type String
28883      */
28884     lastText : "Last Page",
28885     /**
28886      * Customizable piece of the default paging text (defaults to "Refresh")
28887      * @type String
28888      */
28889     refreshText : "Refresh",
28890
28891     // private
28892     renderButtons : function(el){
28893         Roo.PagingToolbar.superclass.render.call(this, el);
28894         this.first = this.addButton({
28895             tooltip: this.firstText,
28896             cls: "x-btn-icon x-grid-page-first",
28897             disabled: true,
28898             handler: this.onClick.createDelegate(this, ["first"])
28899         });
28900         this.prev = this.addButton({
28901             tooltip: this.prevText,
28902             cls: "x-btn-icon x-grid-page-prev",
28903             disabled: true,
28904             handler: this.onClick.createDelegate(this, ["prev"])
28905         });
28906         //this.addSeparator();
28907         this.add(this.beforePageText);
28908         this.field = Roo.get(this.addDom({
28909            tag: "input",
28910            type: "text",
28911            size: "3",
28912            value: "1",
28913            cls: "x-grid-page-number"
28914         }).el);
28915         this.field.on("keydown", this.onPagingKeydown, this);
28916         this.field.on("focus", function(){this.dom.select();});
28917         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28918         this.field.setHeight(18);
28919         //this.addSeparator();
28920         this.next = this.addButton({
28921             tooltip: this.nextText,
28922             cls: "x-btn-icon x-grid-page-next",
28923             disabled: true,
28924             handler: this.onClick.createDelegate(this, ["next"])
28925         });
28926         this.last = this.addButton({
28927             tooltip: this.lastText,
28928             cls: "x-btn-icon x-grid-page-last",
28929             disabled: true,
28930             handler: this.onClick.createDelegate(this, ["last"])
28931         });
28932         //this.addSeparator();
28933         this.loading = this.addButton({
28934             tooltip: this.refreshText,
28935             cls: "x-btn-icon x-grid-loading",
28936             handler: this.onClick.createDelegate(this, ["refresh"])
28937         });
28938
28939         if(this.displayInfo){
28940             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28941         }
28942     },
28943
28944     // private
28945     updateInfo : function(){
28946         if(this.displayEl){
28947             var count = this.ds.getCount();
28948             var msg = count == 0 ?
28949                 this.emptyMsg :
28950                 String.format(
28951                     this.displayMsg,
28952                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28953                 );
28954             this.displayEl.update(msg);
28955         }
28956     },
28957
28958     // private
28959     onLoad : function(ds, r, o){
28960        this.cursor = o.params ? o.params.start : 0;
28961        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28962
28963        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28964        this.field.dom.value = ap;
28965        this.first.setDisabled(ap == 1);
28966        this.prev.setDisabled(ap == 1);
28967        this.next.setDisabled(ap == ps);
28968        this.last.setDisabled(ap == ps);
28969        this.loading.enable();
28970        this.updateInfo();
28971     },
28972
28973     // private
28974     getPageData : function(){
28975         var total = this.ds.getTotalCount();
28976         return {
28977             total : total,
28978             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28979             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28980         };
28981     },
28982
28983     // private
28984     onLoadError : function(){
28985         this.loading.enable();
28986     },
28987
28988     // private
28989     onPagingKeydown : function(e){
28990         var k = e.getKey();
28991         var d = this.getPageData();
28992         if(k == e.RETURN){
28993             var v = this.field.dom.value, pageNum;
28994             if(!v || isNaN(pageNum = parseInt(v, 10))){
28995                 this.field.dom.value = d.activePage;
28996                 return;
28997             }
28998             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28999             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29000             e.stopEvent();
29001         }
29002         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
29003         {
29004           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29005           this.field.dom.value = pageNum;
29006           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29007           e.stopEvent();
29008         }
29009         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29010         {
29011           var v = this.field.dom.value, pageNum; 
29012           var increment = (e.shiftKey) ? 10 : 1;
29013           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29014             increment *= -1;
29015           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29016             this.field.dom.value = d.activePage;
29017             return;
29018           }
29019           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29020           {
29021             this.field.dom.value = parseInt(v, 10) + increment;
29022             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29023             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29024           }
29025           e.stopEvent();
29026         }
29027     },
29028
29029     // private
29030     beforeLoad : function(){
29031         if(this.loading){
29032             this.loading.disable();
29033         }
29034     },
29035
29036     // private
29037     onClick : function(which){
29038         var ds = this.ds;
29039         switch(which){
29040             case "first":
29041                 ds.load({params:{start: 0, limit: this.pageSize}});
29042             break;
29043             case "prev":
29044                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29045             break;
29046             case "next":
29047                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29048             break;
29049             case "last":
29050                 var total = ds.getTotalCount();
29051                 var extra = total % this.pageSize;
29052                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29053                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29054             break;
29055             case "refresh":
29056                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29057             break;
29058         }
29059     },
29060
29061     /**
29062      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29063      * @param {Roo.data.Store} store The data store to unbind
29064      */
29065     unbind : function(ds){
29066         ds.un("beforeload", this.beforeLoad, this);
29067         ds.un("load", this.onLoad, this);
29068         ds.un("loadexception", this.onLoadError, this);
29069         ds.un("remove", this.updateInfo, this);
29070         ds.un("add", this.updateInfo, this);
29071         this.ds = undefined;
29072     },
29073
29074     /**
29075      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29076      * @param {Roo.data.Store} store The data store to bind
29077      */
29078     bind : function(ds){
29079         ds.on("beforeload", this.beforeLoad, this);
29080         ds.on("load", this.onLoad, this);
29081         ds.on("loadexception", this.onLoadError, this);
29082         ds.on("remove", this.updateInfo, this);
29083         ds.on("add", this.updateInfo, this);
29084         this.ds = ds;
29085     }
29086 });/*
29087  * Based on:
29088  * Ext JS Library 1.1.1
29089  * Copyright(c) 2006-2007, Ext JS, LLC.
29090  *
29091  * Originally Released Under LGPL - original licence link has changed is not relivant.
29092  *
29093  * Fork - LGPL
29094  * <script type="text/javascript">
29095  */
29096
29097 /**
29098  * @class Roo.Resizable
29099  * @extends Roo.util.Observable
29100  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29101  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29102  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
29103  * the element will be wrapped for you automatically.</p>
29104  * <p>Here is the list of valid resize handles:</p>
29105  * <pre>
29106 Value   Description
29107 ------  -------------------
29108  'n'     north
29109  's'     south
29110  'e'     east
29111  'w'     west
29112  'nw'    northwest
29113  'sw'    southwest
29114  'se'    southeast
29115  'ne'    northeast
29116  'hd'    horizontal drag
29117  'all'   all
29118 </pre>
29119  * <p>Here's an example showing the creation of a typical Resizable:</p>
29120  * <pre><code>
29121 var resizer = new Roo.Resizable("element-id", {
29122     handles: 'all',
29123     minWidth: 200,
29124     minHeight: 100,
29125     maxWidth: 500,
29126     maxHeight: 400,
29127     pinned: true
29128 });
29129 resizer.on("resize", myHandler);
29130 </code></pre>
29131  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29132  * resizer.east.setDisplayed(false);</p>
29133  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29134  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29135  * resize operation's new size (defaults to [0, 0])
29136  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29137  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29138  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29139  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29140  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29141  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29142  * @cfg {Number} width The width of the element in pixels (defaults to null)
29143  * @cfg {Number} height The height of the element in pixels (defaults to null)
29144  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29145  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29146  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29147  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29148  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29149  * in favor of the handles config option (defaults to false)
29150  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29151  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29152  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29153  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29154  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29155  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29156  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29157  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29158  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29159  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29160  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29161  * @constructor
29162  * Create a new resizable component
29163  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29164  * @param {Object} config configuration options
29165   */
29166 Roo.Resizable = function(el, config)
29167 {
29168     this.el = Roo.get(el);
29169
29170     if(config && config.wrap){
29171         config.resizeChild = this.el;
29172         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29173         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29174         this.el.setStyle("overflow", "hidden");
29175         this.el.setPositioning(config.resizeChild.getPositioning());
29176         config.resizeChild.clearPositioning();
29177         if(!config.width || !config.height){
29178             var csize = config.resizeChild.getSize();
29179             this.el.setSize(csize.width, csize.height);
29180         }
29181         if(config.pinned && !config.adjustments){
29182             config.adjustments = "auto";
29183         }
29184     }
29185
29186     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29187     this.proxy.unselectable();
29188     this.proxy.enableDisplayMode('block');
29189
29190     Roo.apply(this, config);
29191
29192     if(this.pinned){
29193         this.disableTrackOver = true;
29194         this.el.addClass("x-resizable-pinned");
29195     }
29196     // if the element isn't positioned, make it relative
29197     var position = this.el.getStyle("position");
29198     if(position != "absolute" && position != "fixed"){
29199         this.el.setStyle("position", "relative");
29200     }
29201     if(!this.handles){ // no handles passed, must be legacy style
29202         this.handles = 's,e,se';
29203         if(this.multiDirectional){
29204             this.handles += ',n,w';
29205         }
29206     }
29207     if(this.handles == "all"){
29208         this.handles = "n s e w ne nw se sw";
29209     }
29210     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29211     var ps = Roo.Resizable.positions;
29212     for(var i = 0, len = hs.length; i < len; i++){
29213         if(hs[i] && ps[hs[i]]){
29214             var pos = ps[hs[i]];
29215             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29216         }
29217     }
29218     // legacy
29219     this.corner = this.southeast;
29220     
29221     // updateBox = the box can move..
29222     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29223         this.updateBox = true;
29224     }
29225
29226     this.activeHandle = null;
29227
29228     if(this.resizeChild){
29229         if(typeof this.resizeChild == "boolean"){
29230             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29231         }else{
29232             this.resizeChild = Roo.get(this.resizeChild, true);
29233         }
29234     }
29235     
29236     if(this.adjustments == "auto"){
29237         var rc = this.resizeChild;
29238         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29239         if(rc && (hw || hn)){
29240             rc.position("relative");
29241             rc.setLeft(hw ? hw.el.getWidth() : 0);
29242             rc.setTop(hn ? hn.el.getHeight() : 0);
29243         }
29244         this.adjustments = [
29245             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29246             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29247         ];
29248     }
29249
29250     if(this.draggable){
29251         this.dd = this.dynamic ?
29252             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29253         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29254     }
29255
29256     // public events
29257     this.addEvents({
29258         /**
29259          * @event beforeresize
29260          * Fired before resize is allowed. Set enabled to false to cancel resize.
29261          * @param {Roo.Resizable} this
29262          * @param {Roo.EventObject} e The mousedown event
29263          */
29264         "beforeresize" : true,
29265         /**
29266          * @event resizing
29267          * Fired a resizing.
29268          * @param {Roo.Resizable} this
29269          * @param {Number} x The new x position
29270          * @param {Number} y The new y position
29271          * @param {Number} w The new w width
29272          * @param {Number} h The new h hight
29273          * @param {Roo.EventObject} e The mouseup event
29274          */
29275         "resizing" : true,
29276         /**
29277          * @event resize
29278          * Fired after a resize.
29279          * @param {Roo.Resizable} this
29280          * @param {Number} width The new width
29281          * @param {Number} height The new height
29282          * @param {Roo.EventObject} e The mouseup event
29283          */
29284         "resize" : true
29285     });
29286
29287     if(this.width !== null && this.height !== null){
29288         this.resizeTo(this.width, this.height);
29289     }else{
29290         this.updateChildSize();
29291     }
29292     if(Roo.isIE){
29293         this.el.dom.style.zoom = 1;
29294     }
29295     Roo.Resizable.superclass.constructor.call(this);
29296 };
29297
29298 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29299         resizeChild : false,
29300         adjustments : [0, 0],
29301         minWidth : 5,
29302         minHeight : 5,
29303         maxWidth : 10000,
29304         maxHeight : 10000,
29305         enabled : true,
29306         animate : false,
29307         duration : .35,
29308         dynamic : false,
29309         handles : false,
29310         multiDirectional : false,
29311         disableTrackOver : false,
29312         easing : 'easeOutStrong',
29313         widthIncrement : 0,
29314         heightIncrement : 0,
29315         pinned : false,
29316         width : null,
29317         height : null,
29318         preserveRatio : false,
29319         transparent: false,
29320         minX: 0,
29321         minY: 0,
29322         draggable: false,
29323
29324         /**
29325          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29326          */
29327         constrainTo: undefined,
29328         /**
29329          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29330          */
29331         resizeRegion: undefined,
29332
29333
29334     /**
29335      * Perform a manual resize
29336      * @param {Number} width
29337      * @param {Number} height
29338      */
29339     resizeTo : function(width, height){
29340         this.el.setSize(width, height);
29341         this.updateChildSize();
29342         this.fireEvent("resize", this, width, height, null);
29343     },
29344
29345     // private
29346     startSizing : function(e, handle){
29347         this.fireEvent("beforeresize", this, e);
29348         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29349
29350             if(!this.overlay){
29351                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29352                 this.overlay.unselectable();
29353                 this.overlay.enableDisplayMode("block");
29354                 this.overlay.on("mousemove", this.onMouseMove, this);
29355                 this.overlay.on("mouseup", this.onMouseUp, this);
29356             }
29357             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29358
29359             this.resizing = true;
29360             this.startBox = this.el.getBox();
29361             this.startPoint = e.getXY();
29362             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29363                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29364
29365             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29366             this.overlay.show();
29367
29368             if(this.constrainTo) {
29369                 var ct = Roo.get(this.constrainTo);
29370                 this.resizeRegion = ct.getRegion().adjust(
29371                     ct.getFrameWidth('t'),
29372                     ct.getFrameWidth('l'),
29373                     -ct.getFrameWidth('b'),
29374                     -ct.getFrameWidth('r')
29375                 );
29376             }
29377
29378             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29379             this.proxy.show();
29380             this.proxy.setBox(this.startBox);
29381             if(!this.dynamic){
29382                 this.proxy.setStyle('visibility', 'visible');
29383             }
29384         }
29385     },
29386
29387     // private
29388     onMouseDown : function(handle, e){
29389         if(this.enabled){
29390             e.stopEvent();
29391             this.activeHandle = handle;
29392             this.startSizing(e, handle);
29393         }
29394     },
29395
29396     // private
29397     onMouseUp : function(e){
29398         var size = this.resizeElement();
29399         this.resizing = false;
29400         this.handleOut();
29401         this.overlay.hide();
29402         this.proxy.hide();
29403         this.fireEvent("resize", this, size.width, size.height, e);
29404     },
29405
29406     // private
29407     updateChildSize : function(){
29408         
29409         if(this.resizeChild){
29410             var el = this.el;
29411             var child = this.resizeChild;
29412             var adj = this.adjustments;
29413             if(el.dom.offsetWidth){
29414                 var b = el.getSize(true);
29415                 child.setSize(b.width+adj[0], b.height+adj[1]);
29416             }
29417             // Second call here for IE
29418             // The first call enables instant resizing and
29419             // the second call corrects scroll bars if they
29420             // exist
29421             if(Roo.isIE){
29422                 setTimeout(function(){
29423                     if(el.dom.offsetWidth){
29424                         var b = el.getSize(true);
29425                         child.setSize(b.width+adj[0], b.height+adj[1]);
29426                     }
29427                 }, 10);
29428             }
29429         }
29430     },
29431
29432     // private
29433     snap : function(value, inc, min){
29434         if(!inc || !value) return value;
29435         var newValue = value;
29436         var m = value % inc;
29437         if(m > 0){
29438             if(m > (inc/2)){
29439                 newValue = value + (inc-m);
29440             }else{
29441                 newValue = value - m;
29442             }
29443         }
29444         return Math.max(min, newValue);
29445     },
29446
29447     // private
29448     resizeElement : function(){
29449         var box = this.proxy.getBox();
29450         if(this.updateBox){
29451             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29452         }else{
29453             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29454         }
29455         this.updateChildSize();
29456         if(!this.dynamic){
29457             this.proxy.hide();
29458         }
29459         return box;
29460     },
29461
29462     // private
29463     constrain : function(v, diff, m, mx){
29464         if(v - diff < m){
29465             diff = v - m;
29466         }else if(v - diff > mx){
29467             diff = mx - v;
29468         }
29469         return diff;
29470     },
29471
29472     // private
29473     onMouseMove : function(e){
29474         
29475         if(this.enabled){
29476             try{// try catch so if something goes wrong the user doesn't get hung
29477
29478             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29479                 return;
29480             }
29481
29482             //var curXY = this.startPoint;
29483             var curSize = this.curSize || this.startBox;
29484             var x = this.startBox.x, y = this.startBox.y;
29485             var ox = x, oy = y;
29486             var w = curSize.width, h = curSize.height;
29487             var ow = w, oh = h;
29488             var mw = this.minWidth, mh = this.minHeight;
29489             var mxw = this.maxWidth, mxh = this.maxHeight;
29490             var wi = this.widthIncrement;
29491             var hi = this.heightIncrement;
29492
29493             var eventXY = e.getXY();
29494             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29495             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29496
29497             var pos = this.activeHandle.position;
29498
29499             switch(pos){
29500                 case "east":
29501                     w += diffX;
29502                     w = Math.min(Math.max(mw, w), mxw);
29503                     break;
29504              
29505                 case "south":
29506                     h += diffY;
29507                     h = Math.min(Math.max(mh, h), mxh);
29508                     break;
29509                 case "southeast":
29510                     w += diffX;
29511                     h += diffY;
29512                     w = Math.min(Math.max(mw, w), mxw);
29513                     h = Math.min(Math.max(mh, h), mxh);
29514                     break;
29515                 case "north":
29516                     diffY = this.constrain(h, diffY, mh, mxh);
29517                     y += diffY;
29518                     h -= diffY;
29519                     break;
29520                 case "hdrag":
29521                     
29522                     if (wi) {
29523                         var adiffX = Math.abs(diffX);
29524                         var sub = (adiffX % wi); // how much 
29525                         if (sub > (wi/2)) { // far enough to snap
29526                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29527                         } else {
29528                             // remove difference.. 
29529                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29530                         }
29531                     }
29532                     x += diffX;
29533                     x = Math.max(this.minX, x);
29534                     break;
29535                 case "west":
29536                     diffX = this.constrain(w, diffX, mw, mxw);
29537                     x += diffX;
29538                     w -= diffX;
29539                     break;
29540                 case "northeast":
29541                     w += diffX;
29542                     w = Math.min(Math.max(mw, w), mxw);
29543                     diffY = this.constrain(h, diffY, mh, mxh);
29544                     y += diffY;
29545                     h -= diffY;
29546                     break;
29547                 case "northwest":
29548                     diffX = this.constrain(w, diffX, mw, mxw);
29549                     diffY = this.constrain(h, diffY, mh, mxh);
29550                     y += diffY;
29551                     h -= diffY;
29552                     x += diffX;
29553                     w -= diffX;
29554                     break;
29555                case "southwest":
29556                     diffX = this.constrain(w, diffX, mw, mxw);
29557                     h += diffY;
29558                     h = Math.min(Math.max(mh, h), mxh);
29559                     x += diffX;
29560                     w -= diffX;
29561                     break;
29562             }
29563
29564             var sw = this.snap(w, wi, mw);
29565             var sh = this.snap(h, hi, mh);
29566             if(sw != w || sh != h){
29567                 switch(pos){
29568                     case "northeast":
29569                         y -= sh - h;
29570                     break;
29571                     case "north":
29572                         y -= sh - h;
29573                         break;
29574                     case "southwest":
29575                         x -= sw - w;
29576                     break;
29577                     case "west":
29578                         x -= sw - w;
29579                         break;
29580                     case "northwest":
29581                         x -= sw - w;
29582                         y -= sh - h;
29583                     break;
29584                 }
29585                 w = sw;
29586                 h = sh;
29587             }
29588
29589             if(this.preserveRatio){
29590                 switch(pos){
29591                     case "southeast":
29592                     case "east":
29593                         h = oh * (w/ow);
29594                         h = Math.min(Math.max(mh, h), mxh);
29595                         w = ow * (h/oh);
29596                        break;
29597                     case "south":
29598                         w = ow * (h/oh);
29599                         w = Math.min(Math.max(mw, w), mxw);
29600                         h = oh * (w/ow);
29601                         break;
29602                     case "northeast":
29603                         w = ow * (h/oh);
29604                         w = Math.min(Math.max(mw, w), mxw);
29605                         h = oh * (w/ow);
29606                     break;
29607                     case "north":
29608                         var tw = w;
29609                         w = ow * (h/oh);
29610                         w = Math.min(Math.max(mw, w), mxw);
29611                         h = oh * (w/ow);
29612                         x += (tw - w) / 2;
29613                         break;
29614                     case "southwest":
29615                         h = oh * (w/ow);
29616                         h = Math.min(Math.max(mh, h), mxh);
29617                         var tw = w;
29618                         w = ow * (h/oh);
29619                         x += tw - w;
29620                         break;
29621                     case "west":
29622                         var th = h;
29623                         h = oh * (w/ow);
29624                         h = Math.min(Math.max(mh, h), mxh);
29625                         y += (th - h) / 2;
29626                         var tw = w;
29627                         w = ow * (h/oh);
29628                         x += tw - w;
29629                        break;
29630                     case "northwest":
29631                         var tw = w;
29632                         var th = h;
29633                         h = oh * (w/ow);
29634                         h = Math.min(Math.max(mh, h), mxh);
29635                         w = ow * (h/oh);
29636                         y += th - h;
29637                         x += tw - w;
29638                        break;
29639
29640                 }
29641             }
29642             if (pos == 'hdrag') {
29643                 w = ow;
29644             }
29645             this.proxy.setBounds(x, y, w, h);
29646             if(this.dynamic){
29647                 this.resizeElement();
29648             }
29649             }catch(e){}
29650         }
29651         this.fireEvent("resizing", this, x, y, w, h, e);
29652     },
29653
29654     // private
29655     handleOver : function(){
29656         if(this.enabled){
29657             this.el.addClass("x-resizable-over");
29658         }
29659     },
29660
29661     // private
29662     handleOut : function(){
29663         if(!this.resizing){
29664             this.el.removeClass("x-resizable-over");
29665         }
29666     },
29667
29668     /**
29669      * Returns the element this component is bound to.
29670      * @return {Roo.Element}
29671      */
29672     getEl : function(){
29673         return this.el;
29674     },
29675
29676     /**
29677      * Returns the resizeChild element (or null).
29678      * @return {Roo.Element}
29679      */
29680     getResizeChild : function(){
29681         return this.resizeChild;
29682     },
29683     groupHandler : function()
29684     {
29685         
29686     },
29687     /**
29688      * Destroys this resizable. If the element was wrapped and
29689      * removeEl is not true then the element remains.
29690      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29691      */
29692     destroy : function(removeEl){
29693         this.proxy.remove();
29694         if(this.overlay){
29695             this.overlay.removeAllListeners();
29696             this.overlay.remove();
29697         }
29698         var ps = Roo.Resizable.positions;
29699         for(var k in ps){
29700             if(typeof ps[k] != "function" && this[ps[k]]){
29701                 var h = this[ps[k]];
29702                 h.el.removeAllListeners();
29703                 h.el.remove();
29704             }
29705         }
29706         if(removeEl){
29707             this.el.update("");
29708             this.el.remove();
29709         }
29710     }
29711 });
29712
29713 // private
29714 // hash to map config positions to true positions
29715 Roo.Resizable.positions = {
29716     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29717     hd: "hdrag"
29718 };
29719
29720 // private
29721 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29722     if(!this.tpl){
29723         // only initialize the template if resizable is used
29724         var tpl = Roo.DomHelper.createTemplate(
29725             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29726         );
29727         tpl.compile();
29728         Roo.Resizable.Handle.prototype.tpl = tpl;
29729     }
29730     this.position = pos;
29731     this.rz = rz;
29732     // show north drag fro topdra
29733     var handlepos = pos == 'hdrag' ? 'north' : pos;
29734     
29735     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29736     if (pos == 'hdrag') {
29737         this.el.setStyle('cursor', 'pointer');
29738     }
29739     this.el.unselectable();
29740     if(transparent){
29741         this.el.setOpacity(0);
29742     }
29743     this.el.on("mousedown", this.onMouseDown, this);
29744     if(!disableTrackOver){
29745         this.el.on("mouseover", this.onMouseOver, this);
29746         this.el.on("mouseout", this.onMouseOut, this);
29747     }
29748 };
29749
29750 // private
29751 Roo.Resizable.Handle.prototype = {
29752     afterResize : function(rz){
29753         Roo.log('after?');
29754         // do nothing
29755     },
29756     // private
29757     onMouseDown : function(e){
29758         this.rz.onMouseDown(this, e);
29759     },
29760     // private
29761     onMouseOver : function(e){
29762         this.rz.handleOver(this, e);
29763     },
29764     // private
29765     onMouseOut : function(e){
29766         this.rz.handleOut(this, e);
29767     }
29768 };/*
29769  * Based on:
29770  * Ext JS Library 1.1.1
29771  * Copyright(c) 2006-2007, Ext JS, LLC.
29772  *
29773  * Originally Released Under LGPL - original licence link has changed is not relivant.
29774  *
29775  * Fork - LGPL
29776  * <script type="text/javascript">
29777  */
29778
29779 /**
29780  * @class Roo.Editor
29781  * @extends Roo.Component
29782  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29783  * @constructor
29784  * Create a new Editor
29785  * @param {Roo.form.Field} field The Field object (or descendant)
29786  * @param {Object} config The config object
29787  */
29788 Roo.Editor = function(field, config){
29789     Roo.Editor.superclass.constructor.call(this, config);
29790     this.field = field;
29791     this.addEvents({
29792         /**
29793              * @event beforestartedit
29794              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29795              * false from the handler of this event.
29796              * @param {Editor} this
29797              * @param {Roo.Element} boundEl The underlying element bound to this editor
29798              * @param {Mixed} value The field value being set
29799              */
29800         "beforestartedit" : true,
29801         /**
29802              * @event startedit
29803              * Fires when this editor is displayed
29804              * @param {Roo.Element} boundEl The underlying element bound to this editor
29805              * @param {Mixed} value The starting field value
29806              */
29807         "startedit" : true,
29808         /**
29809              * @event beforecomplete
29810              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29811              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29812              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29813              * event will not fire since no edit actually occurred.
29814              * @param {Editor} this
29815              * @param {Mixed} value The current field value
29816              * @param {Mixed} startValue The original field value
29817              */
29818         "beforecomplete" : true,
29819         /**
29820              * @event complete
29821              * Fires after editing is complete and any changed value has been written to the underlying field.
29822              * @param {Editor} this
29823              * @param {Mixed} value The current field value
29824              * @param {Mixed} startValue The original field value
29825              */
29826         "complete" : true,
29827         /**
29828          * @event specialkey
29829          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29830          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29831          * @param {Roo.form.Field} this
29832          * @param {Roo.EventObject} e The event object
29833          */
29834         "specialkey" : true
29835     });
29836 };
29837
29838 Roo.extend(Roo.Editor, Roo.Component, {
29839     /**
29840      * @cfg {Boolean/String} autosize
29841      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29842      * or "height" to adopt the height only (defaults to false)
29843      */
29844     /**
29845      * @cfg {Boolean} revertInvalid
29846      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29847      * validation fails (defaults to true)
29848      */
29849     /**
29850      * @cfg {Boolean} ignoreNoChange
29851      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29852      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29853      * will never be ignored.
29854      */
29855     /**
29856      * @cfg {Boolean} hideEl
29857      * False to keep the bound element visible while the editor is displayed (defaults to true)
29858      */
29859     /**
29860      * @cfg {Mixed} value
29861      * The data value of the underlying field (defaults to "")
29862      */
29863     value : "",
29864     /**
29865      * @cfg {String} alignment
29866      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29867      */
29868     alignment: "c-c?",
29869     /**
29870      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29871      * for bottom-right shadow (defaults to "frame")
29872      */
29873     shadow : "frame",
29874     /**
29875      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29876      */
29877     constrain : false,
29878     /**
29879      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29880      */
29881     completeOnEnter : false,
29882     /**
29883      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29884      */
29885     cancelOnEsc : false,
29886     /**
29887      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29888      */
29889     updateEl : false,
29890
29891     // private
29892     onRender : function(ct, position){
29893         this.el = new Roo.Layer({
29894             shadow: this.shadow,
29895             cls: "x-editor",
29896             parentEl : ct,
29897             shim : this.shim,
29898             shadowOffset:4,
29899             id: this.id,
29900             constrain: this.constrain
29901         });
29902         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29903         if(this.field.msgTarget != 'title'){
29904             this.field.msgTarget = 'qtip';
29905         }
29906         this.field.render(this.el);
29907         if(Roo.isGecko){
29908             this.field.el.dom.setAttribute('autocomplete', 'off');
29909         }
29910         this.field.on("specialkey", this.onSpecialKey, this);
29911         if(this.swallowKeys){
29912             this.field.el.swallowEvent(['keydown','keypress']);
29913         }
29914         this.field.show();
29915         this.field.on("blur", this.onBlur, this);
29916         if(this.field.grow){
29917             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29918         }
29919     },
29920
29921     onSpecialKey : function(field, e)
29922     {
29923         //Roo.log('editor onSpecialKey');
29924         if(this.completeOnEnter && e.getKey() == e.ENTER){
29925             e.stopEvent();
29926             this.completeEdit();
29927             return;
29928         }
29929         // do not fire special key otherwise it might hide close the editor...
29930         if(e.getKey() == e.ENTER){    
29931             return;
29932         }
29933         if(this.cancelOnEsc && e.getKey() == e.ESC){
29934             this.cancelEdit();
29935             return;
29936         } 
29937         this.fireEvent('specialkey', field, e);
29938     
29939     },
29940
29941     /**
29942      * Starts the editing process and shows the editor.
29943      * @param {String/HTMLElement/Element} el The element to edit
29944      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29945       * to the innerHTML of el.
29946      */
29947     startEdit : function(el, value){
29948         if(this.editing){
29949             this.completeEdit();
29950         }
29951         this.boundEl = Roo.get(el);
29952         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29953         if(!this.rendered){
29954             this.render(this.parentEl || document.body);
29955         }
29956         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29957             return;
29958         }
29959         this.startValue = v;
29960         this.field.setValue(v);
29961         if(this.autoSize){
29962             var sz = this.boundEl.getSize();
29963             switch(this.autoSize){
29964                 case "width":
29965                 this.setSize(sz.width,  "");
29966                 break;
29967                 case "height":
29968                 this.setSize("",  sz.height);
29969                 break;
29970                 default:
29971                 this.setSize(sz.width,  sz.height);
29972             }
29973         }
29974         this.el.alignTo(this.boundEl, this.alignment);
29975         this.editing = true;
29976         if(Roo.QuickTips){
29977             Roo.QuickTips.disable();
29978         }
29979         this.show();
29980     },
29981
29982     /**
29983      * Sets the height and width of this editor.
29984      * @param {Number} width The new width
29985      * @param {Number} height The new height
29986      */
29987     setSize : function(w, h){
29988         this.field.setSize(w, h);
29989         if(this.el){
29990             this.el.sync();
29991         }
29992     },
29993
29994     /**
29995      * Realigns the editor to the bound field based on the current alignment config value.
29996      */
29997     realign : function(){
29998         this.el.alignTo(this.boundEl, this.alignment);
29999     },
30000
30001     /**
30002      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30003      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30004      */
30005     completeEdit : function(remainVisible){
30006         if(!this.editing){
30007             return;
30008         }
30009         var v = this.getValue();
30010         if(this.revertInvalid !== false && !this.field.isValid()){
30011             v = this.startValue;
30012             this.cancelEdit(true);
30013         }
30014         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30015             this.editing = false;
30016             this.hide();
30017             return;
30018         }
30019         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30020             this.editing = false;
30021             if(this.updateEl && this.boundEl){
30022                 this.boundEl.update(v);
30023             }
30024             if(remainVisible !== true){
30025                 this.hide();
30026             }
30027             this.fireEvent("complete", this, v, this.startValue);
30028         }
30029     },
30030
30031     // private
30032     onShow : function(){
30033         this.el.show();
30034         if(this.hideEl !== false){
30035             this.boundEl.hide();
30036         }
30037         this.field.show();
30038         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30039             this.fixIEFocus = true;
30040             this.deferredFocus.defer(50, this);
30041         }else{
30042             this.field.focus();
30043         }
30044         this.fireEvent("startedit", this.boundEl, this.startValue);
30045     },
30046
30047     deferredFocus : function(){
30048         if(this.editing){
30049             this.field.focus();
30050         }
30051     },
30052
30053     /**
30054      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30055      * reverted to the original starting value.
30056      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30057      * cancel (defaults to false)
30058      */
30059     cancelEdit : function(remainVisible){
30060         if(this.editing){
30061             this.setValue(this.startValue);
30062             if(remainVisible !== true){
30063                 this.hide();
30064             }
30065         }
30066     },
30067
30068     // private
30069     onBlur : function(){
30070         if(this.allowBlur !== true && this.editing){
30071             this.completeEdit();
30072         }
30073     },
30074
30075     // private
30076     onHide : function(){
30077         if(this.editing){
30078             this.completeEdit();
30079             return;
30080         }
30081         this.field.blur();
30082         if(this.field.collapse){
30083             this.field.collapse();
30084         }
30085         this.el.hide();
30086         if(this.hideEl !== false){
30087             this.boundEl.show();
30088         }
30089         if(Roo.QuickTips){
30090             Roo.QuickTips.enable();
30091         }
30092     },
30093
30094     /**
30095      * Sets the data value of the editor
30096      * @param {Mixed} value Any valid value supported by the underlying field
30097      */
30098     setValue : function(v){
30099         this.field.setValue(v);
30100     },
30101
30102     /**
30103      * Gets the data value of the editor
30104      * @return {Mixed} The data value
30105      */
30106     getValue : function(){
30107         return this.field.getValue();
30108     }
30109 });/*
30110  * Based on:
30111  * Ext JS Library 1.1.1
30112  * Copyright(c) 2006-2007, Ext JS, LLC.
30113  *
30114  * Originally Released Under LGPL - original licence link has changed is not relivant.
30115  *
30116  * Fork - LGPL
30117  * <script type="text/javascript">
30118  */
30119  
30120 /**
30121  * @class Roo.BasicDialog
30122  * @extends Roo.util.Observable
30123  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30124  * <pre><code>
30125 var dlg = new Roo.BasicDialog("my-dlg", {
30126     height: 200,
30127     width: 300,
30128     minHeight: 100,
30129     minWidth: 150,
30130     modal: true,
30131     proxyDrag: true,
30132     shadow: true
30133 });
30134 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30135 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30136 dlg.addButton('Cancel', dlg.hide, dlg);
30137 dlg.show();
30138 </code></pre>
30139   <b>A Dialog should always be a direct child of the body element.</b>
30140  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30141  * @cfg {String} title Default text to display in the title bar (defaults to null)
30142  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30143  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30144  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30145  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30146  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30147  * (defaults to null with no animation)
30148  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30149  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30150  * property for valid values (defaults to 'all')
30151  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30152  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30153  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30154  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30155  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30156  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30157  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30158  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30159  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30160  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30161  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30162  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30163  * draggable = true (defaults to false)
30164  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30165  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30166  * shadow (defaults to false)
30167  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30168  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30169  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30170  * @cfg {Array} buttons Array of buttons
30171  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30172  * @constructor
30173  * Create a new BasicDialog.
30174  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30175  * @param {Object} config Configuration options
30176  */
30177 Roo.BasicDialog = function(el, config){
30178     this.el = Roo.get(el);
30179     var dh = Roo.DomHelper;
30180     if(!this.el && config && config.autoCreate){
30181         if(typeof config.autoCreate == "object"){
30182             if(!config.autoCreate.id){
30183                 config.autoCreate.id = el;
30184             }
30185             this.el = dh.append(document.body,
30186                         config.autoCreate, true);
30187         }else{
30188             this.el = dh.append(document.body,
30189                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30190         }
30191     }
30192     el = this.el;
30193     el.setDisplayed(true);
30194     el.hide = this.hideAction;
30195     this.id = el.id;
30196     el.addClass("x-dlg");
30197
30198     Roo.apply(this, config);
30199
30200     this.proxy = el.createProxy("x-dlg-proxy");
30201     this.proxy.hide = this.hideAction;
30202     this.proxy.setOpacity(.5);
30203     this.proxy.hide();
30204
30205     if(config.width){
30206         el.setWidth(config.width);
30207     }
30208     if(config.height){
30209         el.setHeight(config.height);
30210     }
30211     this.size = el.getSize();
30212     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30213         this.xy = [config.x,config.y];
30214     }else{
30215         this.xy = el.getCenterXY(true);
30216     }
30217     /** The header element @type Roo.Element */
30218     this.header = el.child("> .x-dlg-hd");
30219     /** The body element @type Roo.Element */
30220     this.body = el.child("> .x-dlg-bd");
30221     /** The footer element @type Roo.Element */
30222     this.footer = el.child("> .x-dlg-ft");
30223
30224     if(!this.header){
30225         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30226     }
30227     if(!this.body){
30228         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30229     }
30230
30231     this.header.unselectable();
30232     if(this.title){
30233         this.header.update(this.title);
30234     }
30235     // this element allows the dialog to be focused for keyboard event
30236     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30237     this.focusEl.swallowEvent("click", true);
30238
30239     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30240
30241     // wrap the body and footer for special rendering
30242     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30243     if(this.footer){
30244         this.bwrap.dom.appendChild(this.footer.dom);
30245     }
30246
30247     this.bg = this.el.createChild({
30248         tag: "div", cls:"x-dlg-bg",
30249         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30250     });
30251     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30252
30253
30254     if(this.autoScroll !== false && !this.autoTabs){
30255         this.body.setStyle("overflow", "auto");
30256     }
30257
30258     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30259
30260     if(this.closable !== false){
30261         this.el.addClass("x-dlg-closable");
30262         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30263         this.close.on("click", this.closeClick, this);
30264         this.close.addClassOnOver("x-dlg-close-over");
30265     }
30266     if(this.collapsible !== false){
30267         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30268         this.collapseBtn.on("click", this.collapseClick, this);
30269         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30270         this.header.on("dblclick", this.collapseClick, this);
30271     }
30272     if(this.resizable !== false){
30273         this.el.addClass("x-dlg-resizable");
30274         this.resizer = new Roo.Resizable(el, {
30275             minWidth: this.minWidth || 80,
30276             minHeight:this.minHeight || 80,
30277             handles: this.resizeHandles || "all",
30278             pinned: true
30279         });
30280         this.resizer.on("beforeresize", this.beforeResize, this);
30281         this.resizer.on("resize", this.onResize, this);
30282     }
30283     if(this.draggable !== false){
30284         el.addClass("x-dlg-draggable");
30285         if (!this.proxyDrag) {
30286             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30287         }
30288         else {
30289             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30290         }
30291         dd.setHandleElId(this.header.id);
30292         dd.endDrag = this.endMove.createDelegate(this);
30293         dd.startDrag = this.startMove.createDelegate(this);
30294         dd.onDrag = this.onDrag.createDelegate(this);
30295         dd.scroll = false;
30296         this.dd = dd;
30297     }
30298     if(this.modal){
30299         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30300         this.mask.enableDisplayMode("block");
30301         this.mask.hide();
30302         this.el.addClass("x-dlg-modal");
30303     }
30304     if(this.shadow){
30305         this.shadow = new Roo.Shadow({
30306             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30307             offset : this.shadowOffset
30308         });
30309     }else{
30310         this.shadowOffset = 0;
30311     }
30312     if(Roo.useShims && this.shim !== false){
30313         this.shim = this.el.createShim();
30314         this.shim.hide = this.hideAction;
30315         this.shim.hide();
30316     }else{
30317         this.shim = false;
30318     }
30319     if(this.autoTabs){
30320         this.initTabs();
30321     }
30322     if (this.buttons) { 
30323         var bts= this.buttons;
30324         this.buttons = [];
30325         Roo.each(bts, function(b) {
30326             this.addButton(b);
30327         }, this);
30328     }
30329     
30330     
30331     this.addEvents({
30332         /**
30333          * @event keydown
30334          * Fires when a key is pressed
30335          * @param {Roo.BasicDialog} this
30336          * @param {Roo.EventObject} e
30337          */
30338         "keydown" : true,
30339         /**
30340          * @event move
30341          * Fires when this dialog is moved by the user.
30342          * @param {Roo.BasicDialog} this
30343          * @param {Number} x The new page X
30344          * @param {Number} y The new page Y
30345          */
30346         "move" : true,
30347         /**
30348          * @event resize
30349          * Fires when this dialog is resized by the user.
30350          * @param {Roo.BasicDialog} this
30351          * @param {Number} width The new width
30352          * @param {Number} height The new height
30353          */
30354         "resize" : true,
30355         /**
30356          * @event beforehide
30357          * Fires before this dialog is hidden.
30358          * @param {Roo.BasicDialog} this
30359          */
30360         "beforehide" : true,
30361         /**
30362          * @event hide
30363          * Fires when this dialog is hidden.
30364          * @param {Roo.BasicDialog} this
30365          */
30366         "hide" : true,
30367         /**
30368          * @event beforeshow
30369          * Fires before this dialog is shown.
30370          * @param {Roo.BasicDialog} this
30371          */
30372         "beforeshow" : true,
30373         /**
30374          * @event show
30375          * Fires when this dialog is shown.
30376          * @param {Roo.BasicDialog} this
30377          */
30378         "show" : true
30379     });
30380     el.on("keydown", this.onKeyDown, this);
30381     el.on("mousedown", this.toFront, this);
30382     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30383     this.el.hide();
30384     Roo.DialogManager.register(this);
30385     Roo.BasicDialog.superclass.constructor.call(this);
30386 };
30387
30388 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30389     shadowOffset: Roo.isIE ? 6 : 5,
30390     minHeight: 80,
30391     minWidth: 200,
30392     minButtonWidth: 75,
30393     defaultButton: null,
30394     buttonAlign: "right",
30395     tabTag: 'div',
30396     firstShow: true,
30397
30398     /**
30399      * Sets the dialog title text
30400      * @param {String} text The title text to display
30401      * @return {Roo.BasicDialog} this
30402      */
30403     setTitle : function(text){
30404         this.header.update(text);
30405         return this;
30406     },
30407
30408     // private
30409     closeClick : function(){
30410         this.hide();
30411     },
30412
30413     // private
30414     collapseClick : function(){
30415         this[this.collapsed ? "expand" : "collapse"]();
30416     },
30417
30418     /**
30419      * Collapses the dialog to its minimized state (only the title bar is visible).
30420      * Equivalent to the user clicking the collapse dialog button.
30421      */
30422     collapse : function(){
30423         if(!this.collapsed){
30424             this.collapsed = true;
30425             this.el.addClass("x-dlg-collapsed");
30426             this.restoreHeight = this.el.getHeight();
30427             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30428         }
30429     },
30430
30431     /**
30432      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30433      * clicking the expand dialog button.
30434      */
30435     expand : function(){
30436         if(this.collapsed){
30437             this.collapsed = false;
30438             this.el.removeClass("x-dlg-collapsed");
30439             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30440         }
30441     },
30442
30443     /**
30444      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30445      * @return {Roo.TabPanel} The tabs component
30446      */
30447     initTabs : function(){
30448         var tabs = this.getTabs();
30449         while(tabs.getTab(0)){
30450             tabs.removeTab(0);
30451         }
30452         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30453             var dom = el.dom;
30454             tabs.addTab(Roo.id(dom), dom.title);
30455             dom.title = "";
30456         });
30457         tabs.activate(0);
30458         return tabs;
30459     },
30460
30461     // private
30462     beforeResize : function(){
30463         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30464     },
30465
30466     // private
30467     onResize : function(){
30468         this.refreshSize();
30469         this.syncBodyHeight();
30470         this.adjustAssets();
30471         this.focus();
30472         this.fireEvent("resize", this, this.size.width, this.size.height);
30473     },
30474
30475     // private
30476     onKeyDown : function(e){
30477         if(this.isVisible()){
30478             this.fireEvent("keydown", this, e);
30479         }
30480     },
30481
30482     /**
30483      * Resizes the dialog.
30484      * @param {Number} width
30485      * @param {Number} height
30486      * @return {Roo.BasicDialog} this
30487      */
30488     resizeTo : function(width, height){
30489         this.el.setSize(width, height);
30490         this.size = {width: width, height: height};
30491         this.syncBodyHeight();
30492         if(this.fixedcenter){
30493             this.center();
30494         }
30495         if(this.isVisible()){
30496             this.constrainXY();
30497             this.adjustAssets();
30498         }
30499         this.fireEvent("resize", this, width, height);
30500         return this;
30501     },
30502
30503
30504     /**
30505      * Resizes the dialog to fit the specified content size.
30506      * @param {Number} width
30507      * @param {Number} height
30508      * @return {Roo.BasicDialog} this
30509      */
30510     setContentSize : function(w, h){
30511         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30512         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30513         //if(!this.el.isBorderBox()){
30514             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30515             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30516         //}
30517         if(this.tabs){
30518             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30519             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30520         }
30521         this.resizeTo(w, h);
30522         return this;
30523     },
30524
30525     /**
30526      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30527      * executed in response to a particular key being pressed while the dialog is active.
30528      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30529      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30530      * @param {Function} fn The function to call
30531      * @param {Object} scope (optional) The scope of the function
30532      * @return {Roo.BasicDialog} this
30533      */
30534     addKeyListener : function(key, fn, scope){
30535         var keyCode, shift, ctrl, alt;
30536         if(typeof key == "object" && !(key instanceof Array)){
30537             keyCode = key["key"];
30538             shift = key["shift"];
30539             ctrl = key["ctrl"];
30540             alt = key["alt"];
30541         }else{
30542             keyCode = key;
30543         }
30544         var handler = function(dlg, e){
30545             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30546                 var k = e.getKey();
30547                 if(keyCode instanceof Array){
30548                     for(var i = 0, len = keyCode.length; i < len; i++){
30549                         if(keyCode[i] == k){
30550                           fn.call(scope || window, dlg, k, e);
30551                           return;
30552                         }
30553                     }
30554                 }else{
30555                     if(k == keyCode){
30556                         fn.call(scope || window, dlg, k, e);
30557                     }
30558                 }
30559             }
30560         };
30561         this.on("keydown", handler);
30562         return this;
30563     },
30564
30565     /**
30566      * Returns the TabPanel component (creates it if it doesn't exist).
30567      * Note: If you wish to simply check for the existence of tabs without creating them,
30568      * check for a null 'tabs' property.
30569      * @return {Roo.TabPanel} The tabs component
30570      */
30571     getTabs : function(){
30572         if(!this.tabs){
30573             this.el.addClass("x-dlg-auto-tabs");
30574             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30575             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30576         }
30577         return this.tabs;
30578     },
30579
30580     /**
30581      * Adds a button to the footer section of the dialog.
30582      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30583      * object or a valid Roo.DomHelper element config
30584      * @param {Function} handler The function called when the button is clicked
30585      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30586      * @return {Roo.Button} The new button
30587      */
30588     addButton : function(config, handler, scope){
30589         var dh = Roo.DomHelper;
30590         if(!this.footer){
30591             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30592         }
30593         if(!this.btnContainer){
30594             var tb = this.footer.createChild({
30595
30596                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30597                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30598             }, null, true);
30599             this.btnContainer = tb.firstChild.firstChild.firstChild;
30600         }
30601         var bconfig = {
30602             handler: handler,
30603             scope: scope,
30604             minWidth: this.minButtonWidth,
30605             hideParent:true
30606         };
30607         if(typeof config == "string"){
30608             bconfig.text = config;
30609         }else{
30610             if(config.tag){
30611                 bconfig.dhconfig = config;
30612             }else{
30613                 Roo.apply(bconfig, config);
30614             }
30615         }
30616         var fc = false;
30617         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30618             bconfig.position = Math.max(0, bconfig.position);
30619             fc = this.btnContainer.childNodes[bconfig.position];
30620         }
30621          
30622         var btn = new Roo.Button(
30623             fc ? 
30624                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30625                 : this.btnContainer.appendChild(document.createElement("td")),
30626             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30627             bconfig
30628         );
30629         this.syncBodyHeight();
30630         if(!this.buttons){
30631             /**
30632              * Array of all the buttons that have been added to this dialog via addButton
30633              * @type Array
30634              */
30635             this.buttons = [];
30636         }
30637         this.buttons.push(btn);
30638         return btn;
30639     },
30640
30641     /**
30642      * Sets the default button to be focused when the dialog is displayed.
30643      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30644      * @return {Roo.BasicDialog} this
30645      */
30646     setDefaultButton : function(btn){
30647         this.defaultButton = btn;
30648         return this;
30649     },
30650
30651     // private
30652     getHeaderFooterHeight : function(safe){
30653         var height = 0;
30654         if(this.header){
30655            height += this.header.getHeight();
30656         }
30657         if(this.footer){
30658            var fm = this.footer.getMargins();
30659             height += (this.footer.getHeight()+fm.top+fm.bottom);
30660         }
30661         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30662         height += this.centerBg.getPadding("tb");
30663         return height;
30664     },
30665
30666     // private
30667     syncBodyHeight : function()
30668     {
30669         var bd = this.body, // the text
30670             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30671             bw = this.bwrap;
30672         var height = this.size.height - this.getHeaderFooterHeight(false);
30673         bd.setHeight(height-bd.getMargins("tb"));
30674         var hh = this.header.getHeight();
30675         var h = this.size.height-hh;
30676         cb.setHeight(h);
30677         
30678         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30679         bw.setHeight(h-cb.getPadding("tb"));
30680         
30681         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30682         bd.setWidth(bw.getWidth(true));
30683         if(this.tabs){
30684             this.tabs.syncHeight();
30685             if(Roo.isIE){
30686                 this.tabs.el.repaint();
30687             }
30688         }
30689     },
30690
30691     /**
30692      * Restores the previous state of the dialog if Roo.state is configured.
30693      * @return {Roo.BasicDialog} this
30694      */
30695     restoreState : function(){
30696         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30697         if(box && box.width){
30698             this.xy = [box.x, box.y];
30699             this.resizeTo(box.width, box.height);
30700         }
30701         return this;
30702     },
30703
30704     // private
30705     beforeShow : function(){
30706         this.expand();
30707         if(this.fixedcenter){
30708             this.xy = this.el.getCenterXY(true);
30709         }
30710         if(this.modal){
30711             Roo.get(document.body).addClass("x-body-masked");
30712             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30713             this.mask.show();
30714         }
30715         this.constrainXY();
30716     },
30717
30718     // private
30719     animShow : function(){
30720         var b = Roo.get(this.animateTarget).getBox();
30721         this.proxy.setSize(b.width, b.height);
30722         this.proxy.setLocation(b.x, b.y);
30723         this.proxy.show();
30724         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30725                     true, .35, this.showEl.createDelegate(this));
30726     },
30727
30728     /**
30729      * Shows the dialog.
30730      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30731      * @return {Roo.BasicDialog} this
30732      */
30733     show : function(animateTarget){
30734         if (this.fireEvent("beforeshow", this) === false){
30735             return;
30736         }
30737         if(this.syncHeightBeforeShow){
30738             this.syncBodyHeight();
30739         }else if(this.firstShow){
30740             this.firstShow = false;
30741             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30742         }
30743         this.animateTarget = animateTarget || this.animateTarget;
30744         if(!this.el.isVisible()){
30745             this.beforeShow();
30746             if(this.animateTarget && Roo.get(this.animateTarget)){
30747                 this.animShow();
30748             }else{
30749                 this.showEl();
30750             }
30751         }
30752         return this;
30753     },
30754
30755     // private
30756     showEl : function(){
30757         this.proxy.hide();
30758         this.el.setXY(this.xy);
30759         this.el.show();
30760         this.adjustAssets(true);
30761         this.toFront();
30762         this.focus();
30763         // IE peekaboo bug - fix found by Dave Fenwick
30764         if(Roo.isIE){
30765             this.el.repaint();
30766         }
30767         this.fireEvent("show", this);
30768     },
30769
30770     /**
30771      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30772      * dialog itself will receive focus.
30773      */
30774     focus : function(){
30775         if(this.defaultButton){
30776             this.defaultButton.focus();
30777         }else{
30778             this.focusEl.focus();
30779         }
30780     },
30781
30782     // private
30783     constrainXY : function(){
30784         if(this.constraintoviewport !== false){
30785             if(!this.viewSize){
30786                 if(this.container){
30787                     var s = this.container.getSize();
30788                     this.viewSize = [s.width, s.height];
30789                 }else{
30790                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30791                 }
30792             }
30793             var s = Roo.get(this.container||document).getScroll();
30794
30795             var x = this.xy[0], y = this.xy[1];
30796             var w = this.size.width, h = this.size.height;
30797             var vw = this.viewSize[0], vh = this.viewSize[1];
30798             // only move it if it needs it
30799             var moved = false;
30800             // first validate right/bottom
30801             if(x + w > vw+s.left){
30802                 x = vw - w;
30803                 moved = true;
30804             }
30805             if(y + h > vh+s.top){
30806                 y = vh - h;
30807                 moved = true;
30808             }
30809             // then make sure top/left isn't negative
30810             if(x < s.left){
30811                 x = s.left;
30812                 moved = true;
30813             }
30814             if(y < s.top){
30815                 y = s.top;
30816                 moved = true;
30817             }
30818             if(moved){
30819                 // cache xy
30820                 this.xy = [x, y];
30821                 if(this.isVisible()){
30822                     this.el.setLocation(x, y);
30823                     this.adjustAssets();
30824                 }
30825             }
30826         }
30827     },
30828
30829     // private
30830     onDrag : function(){
30831         if(!this.proxyDrag){
30832             this.xy = this.el.getXY();
30833             this.adjustAssets();
30834         }
30835     },
30836
30837     // private
30838     adjustAssets : function(doShow){
30839         var x = this.xy[0], y = this.xy[1];
30840         var w = this.size.width, h = this.size.height;
30841         if(doShow === true){
30842             if(this.shadow){
30843                 this.shadow.show(this.el);
30844             }
30845             if(this.shim){
30846                 this.shim.show();
30847             }
30848         }
30849         if(this.shadow && this.shadow.isVisible()){
30850             this.shadow.show(this.el);
30851         }
30852         if(this.shim && this.shim.isVisible()){
30853             this.shim.setBounds(x, y, w, h);
30854         }
30855     },
30856
30857     // private
30858     adjustViewport : function(w, h){
30859         if(!w || !h){
30860             w = Roo.lib.Dom.getViewWidth();
30861             h = Roo.lib.Dom.getViewHeight();
30862         }
30863         // cache the size
30864         this.viewSize = [w, h];
30865         if(this.modal && this.mask.isVisible()){
30866             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30867             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30868         }
30869         if(this.isVisible()){
30870             this.constrainXY();
30871         }
30872     },
30873
30874     /**
30875      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30876      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30877      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30878      */
30879     destroy : function(removeEl){
30880         if(this.isVisible()){
30881             this.animateTarget = null;
30882             this.hide();
30883         }
30884         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30885         if(this.tabs){
30886             this.tabs.destroy(removeEl);
30887         }
30888         Roo.destroy(
30889              this.shim,
30890              this.proxy,
30891              this.resizer,
30892              this.close,
30893              this.mask
30894         );
30895         if(this.dd){
30896             this.dd.unreg();
30897         }
30898         if(this.buttons){
30899            for(var i = 0, len = this.buttons.length; i < len; i++){
30900                this.buttons[i].destroy();
30901            }
30902         }
30903         this.el.removeAllListeners();
30904         if(removeEl === true){
30905             this.el.update("");
30906             this.el.remove();
30907         }
30908         Roo.DialogManager.unregister(this);
30909     },
30910
30911     // private
30912     startMove : function(){
30913         if(this.proxyDrag){
30914             this.proxy.show();
30915         }
30916         if(this.constraintoviewport !== false){
30917             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30918         }
30919     },
30920
30921     // private
30922     endMove : function(){
30923         if(!this.proxyDrag){
30924             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30925         }else{
30926             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30927             this.proxy.hide();
30928         }
30929         this.refreshSize();
30930         this.adjustAssets();
30931         this.focus();
30932         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30933     },
30934
30935     /**
30936      * Brings this dialog to the front of any other visible dialogs
30937      * @return {Roo.BasicDialog} this
30938      */
30939     toFront : function(){
30940         Roo.DialogManager.bringToFront(this);
30941         return this;
30942     },
30943
30944     /**
30945      * Sends this dialog to the back (under) of any other visible dialogs
30946      * @return {Roo.BasicDialog} this
30947      */
30948     toBack : function(){
30949         Roo.DialogManager.sendToBack(this);
30950         return this;
30951     },
30952
30953     /**
30954      * Centers this dialog in the viewport
30955      * @return {Roo.BasicDialog} this
30956      */
30957     center : function(){
30958         var xy = this.el.getCenterXY(true);
30959         this.moveTo(xy[0], xy[1]);
30960         return this;
30961     },
30962
30963     /**
30964      * Moves the dialog's top-left corner to the specified point
30965      * @param {Number} x
30966      * @param {Number} y
30967      * @return {Roo.BasicDialog} this
30968      */
30969     moveTo : function(x, y){
30970         this.xy = [x,y];
30971         if(this.isVisible()){
30972             this.el.setXY(this.xy);
30973             this.adjustAssets();
30974         }
30975         return this;
30976     },
30977
30978     /**
30979      * Aligns the dialog to the specified element
30980      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30981      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30982      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30983      * @return {Roo.BasicDialog} this
30984      */
30985     alignTo : function(element, position, offsets){
30986         this.xy = this.el.getAlignToXY(element, position, offsets);
30987         if(this.isVisible()){
30988             this.el.setXY(this.xy);
30989             this.adjustAssets();
30990         }
30991         return this;
30992     },
30993
30994     /**
30995      * Anchors an element to another element and realigns it when the window is resized.
30996      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30997      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30998      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30999      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31000      * is a number, it is used as the buffer delay (defaults to 50ms).
31001      * @return {Roo.BasicDialog} this
31002      */
31003     anchorTo : function(el, alignment, offsets, monitorScroll){
31004         var action = function(){
31005             this.alignTo(el, alignment, offsets);
31006         };
31007         Roo.EventManager.onWindowResize(action, this);
31008         var tm = typeof monitorScroll;
31009         if(tm != 'undefined'){
31010             Roo.EventManager.on(window, 'scroll', action, this,
31011                 {buffer: tm == 'number' ? monitorScroll : 50});
31012         }
31013         action.call(this);
31014         return this;
31015     },
31016
31017     /**
31018      * Returns true if the dialog is visible
31019      * @return {Boolean}
31020      */
31021     isVisible : function(){
31022         return this.el.isVisible();
31023     },
31024
31025     // private
31026     animHide : function(callback){
31027         var b = Roo.get(this.animateTarget).getBox();
31028         this.proxy.show();
31029         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31030         this.el.hide();
31031         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31032                     this.hideEl.createDelegate(this, [callback]));
31033     },
31034
31035     /**
31036      * Hides the dialog.
31037      * @param {Function} callback (optional) Function to call when the dialog is hidden
31038      * @return {Roo.BasicDialog} this
31039      */
31040     hide : function(callback){
31041         if (this.fireEvent("beforehide", this) === false){
31042             return;
31043         }
31044         if(this.shadow){
31045             this.shadow.hide();
31046         }
31047         if(this.shim) {
31048           this.shim.hide();
31049         }
31050         // sometimes animateTarget seems to get set.. causing problems...
31051         // this just double checks..
31052         if(this.animateTarget && Roo.get(this.animateTarget)) {
31053            this.animHide(callback);
31054         }else{
31055             this.el.hide();
31056             this.hideEl(callback);
31057         }
31058         return this;
31059     },
31060
31061     // private
31062     hideEl : function(callback){
31063         this.proxy.hide();
31064         if(this.modal){
31065             this.mask.hide();
31066             Roo.get(document.body).removeClass("x-body-masked");
31067         }
31068         this.fireEvent("hide", this);
31069         if(typeof callback == "function"){
31070             callback();
31071         }
31072     },
31073
31074     // private
31075     hideAction : function(){
31076         this.setLeft("-10000px");
31077         this.setTop("-10000px");
31078         this.setStyle("visibility", "hidden");
31079     },
31080
31081     // private
31082     refreshSize : function(){
31083         this.size = this.el.getSize();
31084         this.xy = this.el.getXY();
31085         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31086     },
31087
31088     // private
31089     // z-index is managed by the DialogManager and may be overwritten at any time
31090     setZIndex : function(index){
31091         if(this.modal){
31092             this.mask.setStyle("z-index", index);
31093         }
31094         if(this.shim){
31095             this.shim.setStyle("z-index", ++index);
31096         }
31097         if(this.shadow){
31098             this.shadow.setZIndex(++index);
31099         }
31100         this.el.setStyle("z-index", ++index);
31101         if(this.proxy){
31102             this.proxy.setStyle("z-index", ++index);
31103         }
31104         if(this.resizer){
31105             this.resizer.proxy.setStyle("z-index", ++index);
31106         }
31107
31108         this.lastZIndex = index;
31109     },
31110
31111     /**
31112      * Returns the element for this dialog
31113      * @return {Roo.Element} The underlying dialog Element
31114      */
31115     getEl : function(){
31116         return this.el;
31117     }
31118 });
31119
31120 /**
31121  * @class Roo.DialogManager
31122  * Provides global access to BasicDialogs that have been created and
31123  * support for z-indexing (layering) multiple open dialogs.
31124  */
31125 Roo.DialogManager = function(){
31126     var list = {};
31127     var accessList = [];
31128     var front = null;
31129
31130     // private
31131     var sortDialogs = function(d1, d2){
31132         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31133     };
31134
31135     // private
31136     var orderDialogs = function(){
31137         accessList.sort(sortDialogs);
31138         var seed = Roo.DialogManager.zseed;
31139         for(var i = 0, len = accessList.length; i < len; i++){
31140             var dlg = accessList[i];
31141             if(dlg){
31142                 dlg.setZIndex(seed + (i*10));
31143             }
31144         }
31145     };
31146
31147     return {
31148         /**
31149          * The starting z-index for BasicDialogs (defaults to 9000)
31150          * @type Number The z-index value
31151          */
31152         zseed : 9000,
31153
31154         // private
31155         register : function(dlg){
31156             list[dlg.id] = dlg;
31157             accessList.push(dlg);
31158         },
31159
31160         // private
31161         unregister : function(dlg){
31162             delete list[dlg.id];
31163             var i=0;
31164             var len=0;
31165             if(!accessList.indexOf){
31166                 for(  i = 0, len = accessList.length; i < len; i++){
31167                     if(accessList[i] == dlg){
31168                         accessList.splice(i, 1);
31169                         return;
31170                     }
31171                 }
31172             }else{
31173                  i = accessList.indexOf(dlg);
31174                 if(i != -1){
31175                     accessList.splice(i, 1);
31176                 }
31177             }
31178         },
31179
31180         /**
31181          * Gets a registered dialog by id
31182          * @param {String/Object} id The id of the dialog or a dialog
31183          * @return {Roo.BasicDialog} this
31184          */
31185         get : function(id){
31186             return typeof id == "object" ? id : list[id];
31187         },
31188
31189         /**
31190          * Brings the specified dialog to the front
31191          * @param {String/Object} dlg The id of the dialog or a dialog
31192          * @return {Roo.BasicDialog} this
31193          */
31194         bringToFront : function(dlg){
31195             dlg = this.get(dlg);
31196             if(dlg != front){
31197                 front = dlg;
31198                 dlg._lastAccess = new Date().getTime();
31199                 orderDialogs();
31200             }
31201             return dlg;
31202         },
31203
31204         /**
31205          * Sends the specified dialog to the back
31206          * @param {String/Object} dlg The id of the dialog or a dialog
31207          * @return {Roo.BasicDialog} this
31208          */
31209         sendToBack : function(dlg){
31210             dlg = this.get(dlg);
31211             dlg._lastAccess = -(new Date().getTime());
31212             orderDialogs();
31213             return dlg;
31214         },
31215
31216         /**
31217          * Hides all dialogs
31218          */
31219         hideAll : function(){
31220             for(var id in list){
31221                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31222                     list[id].hide();
31223                 }
31224             }
31225         }
31226     };
31227 }();
31228
31229 /**
31230  * @class Roo.LayoutDialog
31231  * @extends Roo.BasicDialog
31232  * Dialog which provides adjustments for working with a layout in a Dialog.
31233  * Add your necessary layout config options to the dialog's config.<br>
31234  * Example usage (including a nested layout):
31235  * <pre><code>
31236 if(!dialog){
31237     dialog = new Roo.LayoutDialog("download-dlg", {
31238         modal: true,
31239         width:600,
31240         height:450,
31241         shadow:true,
31242         minWidth:500,
31243         minHeight:350,
31244         autoTabs:true,
31245         proxyDrag:true,
31246         // layout config merges with the dialog config
31247         center:{
31248             tabPosition: "top",
31249             alwaysShowTabs: true
31250         }
31251     });
31252     dialog.addKeyListener(27, dialog.hide, dialog);
31253     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31254     dialog.addButton("Build It!", this.getDownload, this);
31255
31256     // we can even add nested layouts
31257     var innerLayout = new Roo.BorderLayout("dl-inner", {
31258         east: {
31259             initialSize: 200,
31260             autoScroll:true,
31261             split:true
31262         },
31263         center: {
31264             autoScroll:true
31265         }
31266     });
31267     innerLayout.beginUpdate();
31268     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31269     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31270     innerLayout.endUpdate(true);
31271
31272     var layout = dialog.getLayout();
31273     layout.beginUpdate();
31274     layout.add("center", new Roo.ContentPanel("standard-panel",
31275                         {title: "Download the Source", fitToFrame:true}));
31276     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31277                {title: "Build your own roo.js"}));
31278     layout.getRegion("center").showPanel(sp);
31279     layout.endUpdate();
31280 }
31281 </code></pre>
31282     * @constructor
31283     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31284     * @param {Object} config configuration options
31285   */
31286 Roo.LayoutDialog = function(el, cfg){
31287     
31288     var config=  cfg;
31289     if (typeof(cfg) == 'undefined') {
31290         config = Roo.apply({}, el);
31291         // not sure why we use documentElement here.. - it should always be body.
31292         // IE7 borks horribly if we use documentElement.
31293         // webkit also does not like documentElement - it creates a body element...
31294         el = Roo.get( document.body || document.documentElement ).createChild();
31295         //config.autoCreate = true;
31296     }
31297     
31298     
31299     config.autoTabs = false;
31300     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31301     this.body.setStyle({overflow:"hidden", position:"relative"});
31302     this.layout = new Roo.BorderLayout(this.body.dom, config);
31303     this.layout.monitorWindowResize = false;
31304     this.el.addClass("x-dlg-auto-layout");
31305     // fix case when center region overwrites center function
31306     this.center = Roo.BasicDialog.prototype.center;
31307     this.on("show", this.layout.layout, this.layout, true);
31308     if (config.items) {
31309         var xitems = config.items;
31310         delete config.items;
31311         Roo.each(xitems, this.addxtype, this);
31312     }
31313     
31314     
31315 };
31316 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31317     /**
31318      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31319      * @deprecated
31320      */
31321     endUpdate : function(){
31322         this.layout.endUpdate();
31323     },
31324
31325     /**
31326      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31327      *  @deprecated
31328      */
31329     beginUpdate : function(){
31330         this.layout.beginUpdate();
31331     },
31332
31333     /**
31334      * Get the BorderLayout for this dialog
31335      * @return {Roo.BorderLayout}
31336      */
31337     getLayout : function(){
31338         return this.layout;
31339     },
31340
31341     showEl : function(){
31342         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31343         if(Roo.isIE7){
31344             this.layout.layout();
31345         }
31346     },
31347
31348     // private
31349     // Use the syncHeightBeforeShow config option to control this automatically
31350     syncBodyHeight : function(){
31351         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31352         if(this.layout){this.layout.layout();}
31353     },
31354     
31355       /**
31356      * Add an xtype element (actually adds to the layout.)
31357      * @return {Object} xdata xtype object data.
31358      */
31359     
31360     addxtype : function(c) {
31361         return this.layout.addxtype(c);
31362     }
31363 });/*
31364  * Based on:
31365  * Ext JS Library 1.1.1
31366  * Copyright(c) 2006-2007, Ext JS, LLC.
31367  *
31368  * Originally Released Under LGPL - original licence link has changed is not relivant.
31369  *
31370  * Fork - LGPL
31371  * <script type="text/javascript">
31372  */
31373  
31374 /**
31375  * @class Roo.MessageBox
31376  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31377  * Example usage:
31378  *<pre><code>
31379 // Basic alert:
31380 Roo.Msg.alert('Status', 'Changes saved successfully.');
31381
31382 // Prompt for user data:
31383 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31384     if (btn == 'ok'){
31385         // process text value...
31386     }
31387 });
31388
31389 // Show a dialog using config options:
31390 Roo.Msg.show({
31391    title:'Save Changes?',
31392    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31393    buttons: Roo.Msg.YESNOCANCEL,
31394    fn: processResult,
31395    animEl: 'elId'
31396 });
31397 </code></pre>
31398  * @singleton
31399  */
31400 Roo.MessageBox = function(){
31401     var dlg, opt, mask, waitTimer;
31402     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31403     var buttons, activeTextEl, bwidth;
31404
31405     // private
31406     var handleButton = function(button){
31407         dlg.hide();
31408         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31409     };
31410
31411     // private
31412     var handleHide = function(){
31413         if(opt && opt.cls){
31414             dlg.el.removeClass(opt.cls);
31415         }
31416         if(waitTimer){
31417             Roo.TaskMgr.stop(waitTimer);
31418             waitTimer = null;
31419         }
31420     };
31421
31422     // private
31423     var updateButtons = function(b){
31424         var width = 0;
31425         if(!b){
31426             buttons["ok"].hide();
31427             buttons["cancel"].hide();
31428             buttons["yes"].hide();
31429             buttons["no"].hide();
31430             dlg.footer.dom.style.display = 'none';
31431             return width;
31432         }
31433         dlg.footer.dom.style.display = '';
31434         for(var k in buttons){
31435             if(typeof buttons[k] != "function"){
31436                 if(b[k]){
31437                     buttons[k].show();
31438                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31439                     width += buttons[k].el.getWidth()+15;
31440                 }else{
31441                     buttons[k].hide();
31442                 }
31443             }
31444         }
31445         return width;
31446     };
31447
31448     // private
31449     var handleEsc = function(d, k, e){
31450         if(opt && opt.closable !== false){
31451             dlg.hide();
31452         }
31453         if(e){
31454             e.stopEvent();
31455         }
31456     };
31457
31458     return {
31459         /**
31460          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31461          * @return {Roo.BasicDialog} The BasicDialog element
31462          */
31463         getDialog : function(){
31464            if(!dlg){
31465                 dlg = new Roo.BasicDialog("x-msg-box", {
31466                     autoCreate : true,
31467                     shadow: true,
31468                     draggable: true,
31469                     resizable:false,
31470                     constraintoviewport:false,
31471                     fixedcenter:true,
31472                     collapsible : false,
31473                     shim:true,
31474                     modal: true,
31475                     width:400, height:100,
31476                     buttonAlign:"center",
31477                     closeClick : function(){
31478                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31479                             handleButton("no");
31480                         }else{
31481                             handleButton("cancel");
31482                         }
31483                     }
31484                 });
31485                 dlg.on("hide", handleHide);
31486                 mask = dlg.mask;
31487                 dlg.addKeyListener(27, handleEsc);
31488                 buttons = {};
31489                 var bt = this.buttonText;
31490                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31491                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31492                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31493                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31494                 bodyEl = dlg.body.createChild({
31495
31496                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31497                 });
31498                 msgEl = bodyEl.dom.firstChild;
31499                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31500                 textboxEl.enableDisplayMode();
31501                 textboxEl.addKeyListener([10,13], function(){
31502                     if(dlg.isVisible() && opt && opt.buttons){
31503                         if(opt.buttons.ok){
31504                             handleButton("ok");
31505                         }else if(opt.buttons.yes){
31506                             handleButton("yes");
31507                         }
31508                     }
31509                 });
31510                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31511                 textareaEl.enableDisplayMode();
31512                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31513                 progressEl.enableDisplayMode();
31514                 var pf = progressEl.dom.firstChild;
31515                 if (pf) {
31516                     pp = Roo.get(pf.firstChild);
31517                     pp.setHeight(pf.offsetHeight);
31518                 }
31519                 
31520             }
31521             return dlg;
31522         },
31523
31524         /**
31525          * Updates the message box body text
31526          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31527          * the XHTML-compliant non-breaking space character '&amp;#160;')
31528          * @return {Roo.MessageBox} This message box
31529          */
31530         updateText : function(text){
31531             if(!dlg.isVisible() && !opt.width){
31532                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31533             }
31534             msgEl.innerHTML = text || '&#160;';
31535       
31536             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31537             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31538             var w = Math.max(
31539                     Math.min(opt.width || cw , this.maxWidth), 
31540                     Math.max(opt.minWidth || this.minWidth, bwidth)
31541             );
31542             if(opt.prompt){
31543                 activeTextEl.setWidth(w);
31544             }
31545             if(dlg.isVisible()){
31546                 dlg.fixedcenter = false;
31547             }
31548             // to big, make it scroll. = But as usual stupid IE does not support
31549             // !important..
31550             
31551             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31552                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31553                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31554             } else {
31555                 bodyEl.dom.style.height = '';
31556                 bodyEl.dom.style.overflowY = '';
31557             }
31558             if (cw > w) {
31559                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31560             } else {
31561                 bodyEl.dom.style.overflowX = '';
31562             }
31563             
31564             dlg.setContentSize(w, bodyEl.getHeight());
31565             if(dlg.isVisible()){
31566                 dlg.fixedcenter = true;
31567             }
31568             return this;
31569         },
31570
31571         /**
31572          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31573          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31574          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31575          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31576          * @return {Roo.MessageBox} This message box
31577          */
31578         updateProgress : function(value, text){
31579             if(text){
31580                 this.updateText(text);
31581             }
31582             if (pp) { // weird bug on my firefox - for some reason this is not defined
31583                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31584             }
31585             return this;
31586         },        
31587
31588         /**
31589          * Returns true if the message box is currently displayed
31590          * @return {Boolean} True if the message box is visible, else false
31591          */
31592         isVisible : function(){
31593             return dlg && dlg.isVisible();  
31594         },
31595
31596         /**
31597          * Hides the message box if it is displayed
31598          */
31599         hide : function(){
31600             if(this.isVisible()){
31601                 dlg.hide();
31602             }  
31603         },
31604
31605         /**
31606          * Displays a new message box, or reinitializes an existing message box, based on the config options
31607          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31608          * The following config object properties are supported:
31609          * <pre>
31610 Property    Type             Description
31611 ----------  ---------------  ------------------------------------------------------------------------------------
31612 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31613                                    closes (defaults to undefined)
31614 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31615                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31616 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31617                                    progress and wait dialogs will ignore this property and always hide the
31618                                    close button as they can only be closed programmatically.
31619 cls               String           A custom CSS class to apply to the message box element
31620 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31621                                    displayed (defaults to 75)
31622 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31623                                    function will be btn (the name of the button that was clicked, if applicable,
31624                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31625                                    Progress and wait dialogs will ignore this option since they do not respond to
31626                                    user actions and can only be closed programmatically, so any required function
31627                                    should be called by the same code after it closes the dialog.
31628 icon              String           A CSS class that provides a background image to be used as an icon for
31629                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31630 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31631 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31632 modal             Boolean          False to allow user interaction with the page while the message box is
31633                                    displayed (defaults to true)
31634 msg               String           A string that will replace the existing message box body text (defaults
31635                                    to the XHTML-compliant non-breaking space character '&#160;')
31636 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31637 progress          Boolean          True to display a progress bar (defaults to false)
31638 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31639 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31640 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31641 title             String           The title text
31642 value             String           The string value to set into the active textbox element if displayed
31643 wait              Boolean          True to display a progress bar (defaults to false)
31644 width             Number           The width of the dialog in pixels
31645 </pre>
31646          *
31647          * Example usage:
31648          * <pre><code>
31649 Roo.Msg.show({
31650    title: 'Address',
31651    msg: 'Please enter your address:',
31652    width: 300,
31653    buttons: Roo.MessageBox.OKCANCEL,
31654    multiline: true,
31655    fn: saveAddress,
31656    animEl: 'addAddressBtn'
31657 });
31658 </code></pre>
31659          * @param {Object} config Configuration options
31660          * @return {Roo.MessageBox} This message box
31661          */
31662         show : function(options)
31663         {
31664             
31665             // this causes nightmares if you show one dialog after another
31666             // especially on callbacks..
31667              
31668             if(this.isVisible()){
31669                 
31670                 this.hide();
31671                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31672                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31673                 Roo.log("New Dialog Message:" +  options.msg )
31674                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31675                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31676                 
31677             }
31678             var d = this.getDialog();
31679             opt = options;
31680             d.setTitle(opt.title || "&#160;");
31681             d.close.setDisplayed(opt.closable !== false);
31682             activeTextEl = textboxEl;
31683             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31684             if(opt.prompt){
31685                 if(opt.multiline){
31686                     textboxEl.hide();
31687                     textareaEl.show();
31688                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31689                         opt.multiline : this.defaultTextHeight);
31690                     activeTextEl = textareaEl;
31691                 }else{
31692                     textboxEl.show();
31693                     textareaEl.hide();
31694                 }
31695             }else{
31696                 textboxEl.hide();
31697                 textareaEl.hide();
31698             }
31699             progressEl.setDisplayed(opt.progress === true);
31700             this.updateProgress(0);
31701             activeTextEl.dom.value = opt.value || "";
31702             if(opt.prompt){
31703                 dlg.setDefaultButton(activeTextEl);
31704             }else{
31705                 var bs = opt.buttons;
31706                 var db = null;
31707                 if(bs && bs.ok){
31708                     db = buttons["ok"];
31709                 }else if(bs && bs.yes){
31710                     db = buttons["yes"];
31711                 }
31712                 dlg.setDefaultButton(db);
31713             }
31714             bwidth = updateButtons(opt.buttons);
31715             this.updateText(opt.msg);
31716             if(opt.cls){
31717                 d.el.addClass(opt.cls);
31718             }
31719             d.proxyDrag = opt.proxyDrag === true;
31720             d.modal = opt.modal !== false;
31721             d.mask = opt.modal !== false ? mask : false;
31722             if(!d.isVisible()){
31723                 // force it to the end of the z-index stack so it gets a cursor in FF
31724                 document.body.appendChild(dlg.el.dom);
31725                 d.animateTarget = null;
31726                 d.show(options.animEl);
31727             }
31728             return this;
31729         },
31730
31731         /**
31732          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31733          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31734          * and closing the message box when the process is complete.
31735          * @param {String} title The title bar text
31736          * @param {String} msg The message box body text
31737          * @return {Roo.MessageBox} This message box
31738          */
31739         progress : function(title, msg){
31740             this.show({
31741                 title : title,
31742                 msg : msg,
31743                 buttons: false,
31744                 progress:true,
31745                 closable:false,
31746                 minWidth: this.minProgressWidth,
31747                 modal : true
31748             });
31749             return this;
31750         },
31751
31752         /**
31753          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31754          * If a callback function is passed it will be called after the user clicks the button, and the
31755          * id of the button that was clicked will be passed as the only parameter to the callback
31756          * (could also be the top-right close button).
31757          * @param {String} title The title bar text
31758          * @param {String} msg The message box body text
31759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31760          * @param {Object} scope (optional) The scope of the callback function
31761          * @return {Roo.MessageBox} This message box
31762          */
31763         alert : function(title, msg, fn, scope){
31764             this.show({
31765                 title : title,
31766                 msg : msg,
31767                 buttons: this.OK,
31768                 fn: fn,
31769                 scope : scope,
31770                 modal : true
31771             });
31772             return this;
31773         },
31774
31775         /**
31776          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31777          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31778          * You are responsible for closing the message box when the process is complete.
31779          * @param {String} msg The message box body text
31780          * @param {String} title (optional) The title bar text
31781          * @return {Roo.MessageBox} This message box
31782          */
31783         wait : function(msg, title){
31784             this.show({
31785                 title : title,
31786                 msg : msg,
31787                 buttons: false,
31788                 closable:false,
31789                 progress:true,
31790                 modal:true,
31791                 width:300,
31792                 wait:true
31793             });
31794             waitTimer = Roo.TaskMgr.start({
31795                 run: function(i){
31796                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31797                 },
31798                 interval: 1000
31799             });
31800             return this;
31801         },
31802
31803         /**
31804          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31805          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31806          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31807          * @param {String} title The title bar text
31808          * @param {String} msg The message box body text
31809          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31810          * @param {Object} scope (optional) The scope of the callback function
31811          * @return {Roo.MessageBox} This message box
31812          */
31813         confirm : function(title, msg, fn, scope){
31814             this.show({
31815                 title : title,
31816                 msg : msg,
31817                 buttons: this.YESNO,
31818                 fn: fn,
31819                 scope : scope,
31820                 modal : true
31821             });
31822             return this;
31823         },
31824
31825         /**
31826          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31827          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31828          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31829          * (could also be the top-right close button) and the text that was entered will be passed as the two
31830          * parameters to the callback.
31831          * @param {String} title The title bar text
31832          * @param {String} msg The message box body text
31833          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31834          * @param {Object} scope (optional) The scope of the callback function
31835          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31836          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31837          * @return {Roo.MessageBox} This message box
31838          */
31839         prompt : function(title, msg, fn, scope, multiline){
31840             this.show({
31841                 title : title,
31842                 msg : msg,
31843                 buttons: this.OKCANCEL,
31844                 fn: fn,
31845                 minWidth:250,
31846                 scope : scope,
31847                 prompt:true,
31848                 multiline: multiline,
31849                 modal : true
31850             });
31851             return this;
31852         },
31853
31854         /**
31855          * Button config that displays a single OK button
31856          * @type Object
31857          */
31858         OK : {ok:true},
31859         /**
31860          * Button config that displays Yes and No buttons
31861          * @type Object
31862          */
31863         YESNO : {yes:true, no:true},
31864         /**
31865          * Button config that displays OK and Cancel buttons
31866          * @type Object
31867          */
31868         OKCANCEL : {ok:true, cancel:true},
31869         /**
31870          * Button config that displays Yes, No and Cancel buttons
31871          * @type Object
31872          */
31873         YESNOCANCEL : {yes:true, no:true, cancel:true},
31874
31875         /**
31876          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31877          * @type Number
31878          */
31879         defaultTextHeight : 75,
31880         /**
31881          * The maximum width in pixels of the message box (defaults to 600)
31882          * @type Number
31883          */
31884         maxWidth : 600,
31885         /**
31886          * The minimum width in pixels of the message box (defaults to 100)
31887          * @type Number
31888          */
31889         minWidth : 100,
31890         /**
31891          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31892          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31893          * @type Number
31894          */
31895         minProgressWidth : 250,
31896         /**
31897          * An object containing the default button text strings that can be overriden for localized language support.
31898          * Supported properties are: ok, cancel, yes and no.
31899          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31900          * @type Object
31901          */
31902         buttonText : {
31903             ok : "OK",
31904             cancel : "Cancel",
31905             yes : "Yes",
31906             no : "No"
31907         }
31908     };
31909 }();
31910
31911 /**
31912  * Shorthand for {@link Roo.MessageBox}
31913  */
31914 Roo.Msg = Roo.MessageBox;/*
31915  * Based on:
31916  * Ext JS Library 1.1.1
31917  * Copyright(c) 2006-2007, Ext JS, LLC.
31918  *
31919  * Originally Released Under LGPL - original licence link has changed is not relivant.
31920  *
31921  * Fork - LGPL
31922  * <script type="text/javascript">
31923  */
31924 /**
31925  * @class Roo.QuickTips
31926  * Provides attractive and customizable tooltips for any element.
31927  * @singleton
31928  */
31929 Roo.QuickTips = function(){
31930     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31931     var ce, bd, xy, dd;
31932     var visible = false, disabled = true, inited = false;
31933     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31934     
31935     var onOver = function(e){
31936         if(disabled){
31937             return;
31938         }
31939         var t = e.getTarget();
31940         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31941             return;
31942         }
31943         if(ce && t == ce.el){
31944             clearTimeout(hideProc);
31945             return;
31946         }
31947         if(t && tagEls[t.id]){
31948             tagEls[t.id].el = t;
31949             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31950             return;
31951         }
31952         var ttp, et = Roo.fly(t);
31953         var ns = cfg.namespace;
31954         if(tm.interceptTitles && t.title){
31955             ttp = t.title;
31956             t.qtip = ttp;
31957             t.removeAttribute("title");
31958             e.preventDefault();
31959         }else{
31960             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31961         }
31962         if(ttp){
31963             showProc = show.defer(tm.showDelay, tm, [{
31964                 el: t, 
31965                 text: ttp, 
31966                 width: et.getAttributeNS(ns, cfg.width),
31967                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31968                 title: et.getAttributeNS(ns, cfg.title),
31969                     cls: et.getAttributeNS(ns, cfg.cls)
31970             }]);
31971         }
31972     };
31973     
31974     var onOut = function(e){
31975         clearTimeout(showProc);
31976         var t = e.getTarget();
31977         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31978             hideProc = setTimeout(hide, tm.hideDelay);
31979         }
31980     };
31981     
31982     var onMove = function(e){
31983         if(disabled){
31984             return;
31985         }
31986         xy = e.getXY();
31987         xy[1] += 18;
31988         if(tm.trackMouse && ce){
31989             el.setXY(xy);
31990         }
31991     };
31992     
31993     var onDown = function(e){
31994         clearTimeout(showProc);
31995         clearTimeout(hideProc);
31996         if(!e.within(el)){
31997             if(tm.hideOnClick){
31998                 hide();
31999                 tm.disable();
32000                 tm.enable.defer(100, tm);
32001             }
32002         }
32003     };
32004     
32005     var getPad = function(){
32006         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32007     };
32008
32009     var show = function(o){
32010         if(disabled){
32011             return;
32012         }
32013         clearTimeout(dismissProc);
32014         ce = o;
32015         if(removeCls){ // in case manually hidden
32016             el.removeClass(removeCls);
32017             removeCls = null;
32018         }
32019         if(ce.cls){
32020             el.addClass(ce.cls);
32021             removeCls = ce.cls;
32022         }
32023         if(ce.title){
32024             tipTitle.update(ce.title);
32025             tipTitle.show();
32026         }else{
32027             tipTitle.update('');
32028             tipTitle.hide();
32029         }
32030         el.dom.style.width  = tm.maxWidth+'px';
32031         //tipBody.dom.style.width = '';
32032         tipBodyText.update(o.text);
32033         var p = getPad(), w = ce.width;
32034         if(!w){
32035             var td = tipBodyText.dom;
32036             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32037             if(aw > tm.maxWidth){
32038                 w = tm.maxWidth;
32039             }else if(aw < tm.minWidth){
32040                 w = tm.minWidth;
32041             }else{
32042                 w = aw;
32043             }
32044         }
32045         //tipBody.setWidth(w);
32046         el.setWidth(parseInt(w, 10) + p);
32047         if(ce.autoHide === false){
32048             close.setDisplayed(true);
32049             if(dd){
32050                 dd.unlock();
32051             }
32052         }else{
32053             close.setDisplayed(false);
32054             if(dd){
32055                 dd.lock();
32056             }
32057         }
32058         if(xy){
32059             el.avoidY = xy[1]-18;
32060             el.setXY(xy);
32061         }
32062         if(tm.animate){
32063             el.setOpacity(.1);
32064             el.setStyle("visibility", "visible");
32065             el.fadeIn({callback: afterShow});
32066         }else{
32067             afterShow();
32068         }
32069     };
32070     
32071     var afterShow = function(){
32072         if(ce){
32073             el.show();
32074             esc.enable();
32075             if(tm.autoDismiss && ce.autoHide !== false){
32076                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32077             }
32078         }
32079     };
32080     
32081     var hide = function(noanim){
32082         clearTimeout(dismissProc);
32083         clearTimeout(hideProc);
32084         ce = null;
32085         if(el.isVisible()){
32086             esc.disable();
32087             if(noanim !== true && tm.animate){
32088                 el.fadeOut({callback: afterHide});
32089             }else{
32090                 afterHide();
32091             } 
32092         }
32093     };
32094     
32095     var afterHide = function(){
32096         el.hide();
32097         if(removeCls){
32098             el.removeClass(removeCls);
32099             removeCls = null;
32100         }
32101     };
32102     
32103     return {
32104         /**
32105         * @cfg {Number} minWidth
32106         * The minimum width of the quick tip (defaults to 40)
32107         */
32108        minWidth : 40,
32109         /**
32110         * @cfg {Number} maxWidth
32111         * The maximum width of the quick tip (defaults to 300)
32112         */
32113        maxWidth : 300,
32114         /**
32115         * @cfg {Boolean} interceptTitles
32116         * True to automatically use the element's DOM title value if available (defaults to false)
32117         */
32118        interceptTitles : false,
32119         /**
32120         * @cfg {Boolean} trackMouse
32121         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32122         */
32123        trackMouse : false,
32124         /**
32125         * @cfg {Boolean} hideOnClick
32126         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32127         */
32128        hideOnClick : true,
32129         /**
32130         * @cfg {Number} showDelay
32131         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32132         */
32133        showDelay : 500,
32134         /**
32135         * @cfg {Number} hideDelay
32136         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32137         */
32138        hideDelay : 200,
32139         /**
32140         * @cfg {Boolean} autoHide
32141         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32142         * Used in conjunction with hideDelay.
32143         */
32144        autoHide : true,
32145         /**
32146         * @cfg {Boolean}
32147         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32148         * (defaults to true).  Used in conjunction with autoDismissDelay.
32149         */
32150        autoDismiss : true,
32151         /**
32152         * @cfg {Number}
32153         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32154         */
32155        autoDismissDelay : 5000,
32156        /**
32157         * @cfg {Boolean} animate
32158         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32159         */
32160        animate : false,
32161
32162        /**
32163         * @cfg {String} title
32164         * Title text to display (defaults to '').  This can be any valid HTML markup.
32165         */
32166         title: '',
32167        /**
32168         * @cfg {String} text
32169         * Body text to display (defaults to '').  This can be any valid HTML markup.
32170         */
32171         text : '',
32172        /**
32173         * @cfg {String} cls
32174         * A CSS class to apply to the base quick tip element (defaults to '').
32175         */
32176         cls : '',
32177        /**
32178         * @cfg {Number} width
32179         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32180         * minWidth or maxWidth.
32181         */
32182         width : null,
32183
32184     /**
32185      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32186      * or display QuickTips in a page.
32187      */
32188        init : function(){
32189           tm = Roo.QuickTips;
32190           cfg = tm.tagConfig;
32191           if(!inited){
32192               if(!Roo.isReady){ // allow calling of init() before onReady
32193                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32194                   return;
32195               }
32196               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32197               el.fxDefaults = {stopFx: true};
32198               // maximum custom styling
32199               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32200               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32201               tipTitle = el.child('h3');
32202               tipTitle.enableDisplayMode("block");
32203               tipBody = el.child('div.x-tip-bd');
32204               tipBodyText = el.child('div.x-tip-bd-inner');
32205               //bdLeft = el.child('div.x-tip-bd-left');
32206               //bdRight = el.child('div.x-tip-bd-right');
32207               close = el.child('div.x-tip-close');
32208               close.enableDisplayMode("block");
32209               close.on("click", hide);
32210               var d = Roo.get(document);
32211               d.on("mousedown", onDown);
32212               d.on("mouseover", onOver);
32213               d.on("mouseout", onOut);
32214               d.on("mousemove", onMove);
32215               esc = d.addKeyListener(27, hide);
32216               esc.disable();
32217               if(Roo.dd.DD){
32218                   dd = el.initDD("default", null, {
32219                       onDrag : function(){
32220                           el.sync();  
32221                       }
32222                   });
32223                   dd.setHandleElId(tipTitle.id);
32224                   dd.lock();
32225               }
32226               inited = true;
32227           }
32228           this.enable(); 
32229        },
32230
32231     /**
32232      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32233      * are supported:
32234      * <pre>
32235 Property    Type                   Description
32236 ----------  ---------------------  ------------------------------------------------------------------------
32237 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32238      * </ul>
32239      * @param {Object} config The config object
32240      */
32241        register : function(config){
32242            var cs = config instanceof Array ? config : arguments;
32243            for(var i = 0, len = cs.length; i < len; i++) {
32244                var c = cs[i];
32245                var target = c.target;
32246                if(target){
32247                    if(target instanceof Array){
32248                        for(var j = 0, jlen = target.length; j < jlen; j++){
32249                            tagEls[target[j]] = c;
32250                        }
32251                    }else{
32252                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32253                    }
32254                }
32255            }
32256        },
32257
32258     /**
32259      * Removes this quick tip from its element and destroys it.
32260      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32261      */
32262        unregister : function(el){
32263            delete tagEls[Roo.id(el)];
32264        },
32265
32266     /**
32267      * Enable this quick tip.
32268      */
32269        enable : function(){
32270            if(inited && disabled){
32271                locks.pop();
32272                if(locks.length < 1){
32273                    disabled = false;
32274                }
32275            }
32276        },
32277
32278     /**
32279      * Disable this quick tip.
32280      */
32281        disable : function(){
32282           disabled = true;
32283           clearTimeout(showProc);
32284           clearTimeout(hideProc);
32285           clearTimeout(dismissProc);
32286           if(ce){
32287               hide(true);
32288           }
32289           locks.push(1);
32290        },
32291
32292     /**
32293      * Returns true if the quick tip is enabled, else false.
32294      */
32295        isEnabled : function(){
32296             return !disabled;
32297        },
32298
32299         // private
32300        tagConfig : {
32301            namespace : "ext",
32302            attribute : "qtip",
32303            width : "width",
32304            target : "target",
32305            title : "qtitle",
32306            hide : "hide",
32307            cls : "qclass"
32308        }
32309    };
32310 }();
32311
32312 // backwards compat
32313 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32314  * Based on:
32315  * Ext JS Library 1.1.1
32316  * Copyright(c) 2006-2007, Ext JS, LLC.
32317  *
32318  * Originally Released Under LGPL - original licence link has changed is not relivant.
32319  *
32320  * Fork - LGPL
32321  * <script type="text/javascript">
32322  */
32323  
32324
32325 /**
32326  * @class Roo.tree.TreePanel
32327  * @extends Roo.data.Tree
32328
32329  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32330  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32331  * @cfg {Boolean} enableDD true to enable drag and drop
32332  * @cfg {Boolean} enableDrag true to enable just drag
32333  * @cfg {Boolean} enableDrop true to enable just drop
32334  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32335  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32336  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32337  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32338  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32339  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32340  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32341  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32342  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32343  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32344  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32345  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32346  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32347  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32348  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32349  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32350  * 
32351  * @constructor
32352  * @param {String/HTMLElement/Element} el The container element
32353  * @param {Object} config
32354  */
32355 Roo.tree.TreePanel = function(el, config){
32356     var root = false;
32357     var loader = false;
32358     if (config.root) {
32359         root = config.root;
32360         delete config.root;
32361     }
32362     if (config.loader) {
32363         loader = config.loader;
32364         delete config.loader;
32365     }
32366     
32367     Roo.apply(this, config);
32368     Roo.tree.TreePanel.superclass.constructor.call(this);
32369     this.el = Roo.get(el);
32370     this.el.addClass('x-tree');
32371     //console.log(root);
32372     if (root) {
32373         this.setRootNode( Roo.factory(root, Roo.tree));
32374     }
32375     if (loader) {
32376         this.loader = Roo.factory(loader, Roo.tree);
32377     }
32378    /**
32379     * Read-only. The id of the container element becomes this TreePanel's id.
32380     */
32381     this.id = this.el.id;
32382     this.addEvents({
32383         /**
32384         * @event beforeload
32385         * Fires before a node is loaded, return false to cancel
32386         * @param {Node} node The node being loaded
32387         */
32388         "beforeload" : true,
32389         /**
32390         * @event load
32391         * Fires when a node is loaded
32392         * @param {Node} node The node that was loaded
32393         */
32394         "load" : true,
32395         /**
32396         * @event textchange
32397         * Fires when the text for a node is changed
32398         * @param {Node} node The node
32399         * @param {String} text The new text
32400         * @param {String} oldText The old text
32401         */
32402         "textchange" : true,
32403         /**
32404         * @event beforeexpand
32405         * Fires before a node is expanded, return false to cancel.
32406         * @param {Node} node The node
32407         * @param {Boolean} deep
32408         * @param {Boolean} anim
32409         */
32410         "beforeexpand" : true,
32411         /**
32412         * @event beforecollapse
32413         * Fires before a node is collapsed, return false to cancel.
32414         * @param {Node} node The node
32415         * @param {Boolean} deep
32416         * @param {Boolean} anim
32417         */
32418         "beforecollapse" : true,
32419         /**
32420         * @event expand
32421         * Fires when a node is expanded
32422         * @param {Node} node The node
32423         */
32424         "expand" : true,
32425         /**
32426         * @event disabledchange
32427         * Fires when the disabled status of a node changes
32428         * @param {Node} node The node
32429         * @param {Boolean} disabled
32430         */
32431         "disabledchange" : true,
32432         /**
32433         * @event collapse
32434         * Fires when a node is collapsed
32435         * @param {Node} node The node
32436         */
32437         "collapse" : true,
32438         /**
32439         * @event beforeclick
32440         * Fires before click processing on a node. Return false to cancel the default action.
32441         * @param {Node} node The node
32442         * @param {Roo.EventObject} e The event object
32443         */
32444         "beforeclick":true,
32445         /**
32446         * @event checkchange
32447         * Fires when a node with a checkbox's checked property changes
32448         * @param {Node} this This node
32449         * @param {Boolean} checked
32450         */
32451         "checkchange":true,
32452         /**
32453         * @event click
32454         * Fires when a node is clicked
32455         * @param {Node} node The node
32456         * @param {Roo.EventObject} e The event object
32457         */
32458         "click":true,
32459         /**
32460         * @event dblclick
32461         * Fires when a node is double clicked
32462         * @param {Node} node The node
32463         * @param {Roo.EventObject} e The event object
32464         */
32465         "dblclick":true,
32466         /**
32467         * @event contextmenu
32468         * Fires when a node is right clicked
32469         * @param {Node} node The node
32470         * @param {Roo.EventObject} e The event object
32471         */
32472         "contextmenu":true,
32473         /**
32474         * @event beforechildrenrendered
32475         * Fires right before the child nodes for a node are rendered
32476         * @param {Node} node The node
32477         */
32478         "beforechildrenrendered":true,
32479         /**
32480         * @event startdrag
32481         * Fires when a node starts being dragged
32482         * @param {Roo.tree.TreePanel} this
32483         * @param {Roo.tree.TreeNode} node
32484         * @param {event} e The raw browser event
32485         */ 
32486        "startdrag" : true,
32487        /**
32488         * @event enddrag
32489         * Fires when a drag operation is complete
32490         * @param {Roo.tree.TreePanel} this
32491         * @param {Roo.tree.TreeNode} node
32492         * @param {event} e The raw browser event
32493         */
32494        "enddrag" : true,
32495        /**
32496         * @event dragdrop
32497         * Fires when a dragged node is dropped on a valid DD target
32498         * @param {Roo.tree.TreePanel} this
32499         * @param {Roo.tree.TreeNode} node
32500         * @param {DD} dd The dd it was dropped on
32501         * @param {event} e The raw browser event
32502         */
32503        "dragdrop" : true,
32504        /**
32505         * @event beforenodedrop
32506         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32507         * passed to handlers has the following properties:<br />
32508         * <ul style="padding:5px;padding-left:16px;">
32509         * <li>tree - The TreePanel</li>
32510         * <li>target - The node being targeted for the drop</li>
32511         * <li>data - The drag data from the drag source</li>
32512         * <li>point - The point of the drop - append, above or below</li>
32513         * <li>source - The drag source</li>
32514         * <li>rawEvent - Raw mouse event</li>
32515         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32516         * to be inserted by setting them on this object.</li>
32517         * <li>cancel - Set this to true to cancel the drop.</li>
32518         * </ul>
32519         * @param {Object} dropEvent
32520         */
32521        "beforenodedrop" : true,
32522        /**
32523         * @event nodedrop
32524         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32525         * passed to handlers has the following properties:<br />
32526         * <ul style="padding:5px;padding-left:16px;">
32527         * <li>tree - The TreePanel</li>
32528         * <li>target - The node being targeted for the drop</li>
32529         * <li>data - The drag data from the drag source</li>
32530         * <li>point - The point of the drop - append, above or below</li>
32531         * <li>source - The drag source</li>
32532         * <li>rawEvent - Raw mouse event</li>
32533         * <li>dropNode - Dropped node(s).</li>
32534         * </ul>
32535         * @param {Object} dropEvent
32536         */
32537        "nodedrop" : true,
32538         /**
32539         * @event nodedragover
32540         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32541         * passed to handlers has the following properties:<br />
32542         * <ul style="padding:5px;padding-left:16px;">
32543         * <li>tree - The TreePanel</li>
32544         * <li>target - The node being targeted for the drop</li>
32545         * <li>data - The drag data from the drag source</li>
32546         * <li>point - The point of the drop - append, above or below</li>
32547         * <li>source - The drag source</li>
32548         * <li>rawEvent - Raw mouse event</li>
32549         * <li>dropNode - Drop node(s) provided by the source.</li>
32550         * <li>cancel - Set this to true to signal drop not allowed.</li>
32551         * </ul>
32552         * @param {Object} dragOverEvent
32553         */
32554        "nodedragover" : true
32555         
32556     });
32557     if(this.singleExpand){
32558        this.on("beforeexpand", this.restrictExpand, this);
32559     }
32560     if (this.editor) {
32561         this.editor.tree = this;
32562         this.editor = Roo.factory(this.editor, Roo.tree);
32563     }
32564     
32565     if (this.selModel) {
32566         this.selModel = Roo.factory(this.selModel, Roo.tree);
32567     }
32568    
32569 };
32570 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32571     rootVisible : true,
32572     animate: Roo.enableFx,
32573     lines : true,
32574     enableDD : false,
32575     hlDrop : Roo.enableFx,
32576   
32577     renderer: false,
32578     
32579     rendererTip: false,
32580     // private
32581     restrictExpand : function(node){
32582         var p = node.parentNode;
32583         if(p){
32584             if(p.expandedChild && p.expandedChild.parentNode == p){
32585                 p.expandedChild.collapse();
32586             }
32587             p.expandedChild = node;
32588         }
32589     },
32590
32591     // private override
32592     setRootNode : function(node){
32593         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32594         if(!this.rootVisible){
32595             node.ui = new Roo.tree.RootTreeNodeUI(node);
32596         }
32597         return node;
32598     },
32599
32600     /**
32601      * Returns the container element for this TreePanel
32602      */
32603     getEl : function(){
32604         return this.el;
32605     },
32606
32607     /**
32608      * Returns the default TreeLoader for this TreePanel
32609      */
32610     getLoader : function(){
32611         return this.loader;
32612     },
32613
32614     /**
32615      * Expand all nodes
32616      */
32617     expandAll : function(){
32618         this.root.expand(true);
32619     },
32620
32621     /**
32622      * Collapse all nodes
32623      */
32624     collapseAll : function(){
32625         this.root.collapse(true);
32626     },
32627
32628     /**
32629      * Returns the selection model used by this TreePanel
32630      */
32631     getSelectionModel : function(){
32632         if(!this.selModel){
32633             this.selModel = new Roo.tree.DefaultSelectionModel();
32634         }
32635         return this.selModel;
32636     },
32637
32638     /**
32639      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32640      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32641      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32642      * @return {Array}
32643      */
32644     getChecked : function(a, startNode){
32645         startNode = startNode || this.root;
32646         var r = [];
32647         var f = function(){
32648             if(this.attributes.checked){
32649                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32650             }
32651         }
32652         startNode.cascade(f);
32653         return r;
32654     },
32655
32656     /**
32657      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32658      * @param {String} path
32659      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32660      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32661      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32662      */
32663     expandPath : function(path, attr, callback){
32664         attr = attr || "id";
32665         var keys = path.split(this.pathSeparator);
32666         var curNode = this.root;
32667         if(curNode.attributes[attr] != keys[1]){ // invalid root
32668             if(callback){
32669                 callback(false, null);
32670             }
32671             return;
32672         }
32673         var index = 1;
32674         var f = function(){
32675             if(++index == keys.length){
32676                 if(callback){
32677                     callback(true, curNode);
32678                 }
32679                 return;
32680             }
32681             var c = curNode.findChild(attr, keys[index]);
32682             if(!c){
32683                 if(callback){
32684                     callback(false, curNode);
32685                 }
32686                 return;
32687             }
32688             curNode = c;
32689             c.expand(false, false, f);
32690         };
32691         curNode.expand(false, false, f);
32692     },
32693
32694     /**
32695      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32696      * @param {String} path
32697      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32698      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32699      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32700      */
32701     selectPath : function(path, attr, callback){
32702         attr = attr || "id";
32703         var keys = path.split(this.pathSeparator);
32704         var v = keys.pop();
32705         if(keys.length > 0){
32706             var f = function(success, node){
32707                 if(success && node){
32708                     var n = node.findChild(attr, v);
32709                     if(n){
32710                         n.select();
32711                         if(callback){
32712                             callback(true, n);
32713                         }
32714                     }else if(callback){
32715                         callback(false, n);
32716                     }
32717                 }else{
32718                     if(callback){
32719                         callback(false, n);
32720                     }
32721                 }
32722             };
32723             this.expandPath(keys.join(this.pathSeparator), attr, f);
32724         }else{
32725             this.root.select();
32726             if(callback){
32727                 callback(true, this.root);
32728             }
32729         }
32730     },
32731
32732     getTreeEl : function(){
32733         return this.el;
32734     },
32735
32736     /**
32737      * Trigger rendering of this TreePanel
32738      */
32739     render : function(){
32740         if (this.innerCt) {
32741             return this; // stop it rendering more than once!!
32742         }
32743         
32744         this.innerCt = this.el.createChild({tag:"ul",
32745                cls:"x-tree-root-ct " +
32746                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32747
32748         if(this.containerScroll){
32749             Roo.dd.ScrollManager.register(this.el);
32750         }
32751         if((this.enableDD || this.enableDrop) && !this.dropZone){
32752            /**
32753             * The dropZone used by this tree if drop is enabled
32754             * @type Roo.tree.TreeDropZone
32755             */
32756              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32757                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32758            });
32759         }
32760         if((this.enableDD || this.enableDrag) && !this.dragZone){
32761            /**
32762             * The dragZone used by this tree if drag is enabled
32763             * @type Roo.tree.TreeDragZone
32764             */
32765             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32766                ddGroup: this.ddGroup || "TreeDD",
32767                scroll: this.ddScroll
32768            });
32769         }
32770         this.getSelectionModel().init(this);
32771         if (!this.root) {
32772             Roo.log("ROOT not set in tree");
32773             return this;
32774         }
32775         this.root.render();
32776         if(!this.rootVisible){
32777             this.root.renderChildren();
32778         }
32779         return this;
32780     }
32781 });/*
32782  * Based on:
32783  * Ext JS Library 1.1.1
32784  * Copyright(c) 2006-2007, Ext JS, LLC.
32785  *
32786  * Originally Released Under LGPL - original licence link has changed is not relivant.
32787  *
32788  * Fork - LGPL
32789  * <script type="text/javascript">
32790  */
32791  
32792
32793 /**
32794  * @class Roo.tree.DefaultSelectionModel
32795  * @extends Roo.util.Observable
32796  * The default single selection for a TreePanel.
32797  * @param {Object} cfg Configuration
32798  */
32799 Roo.tree.DefaultSelectionModel = function(cfg){
32800    this.selNode = null;
32801    
32802    
32803    
32804    this.addEvents({
32805        /**
32806         * @event selectionchange
32807         * Fires when the selected node changes
32808         * @param {DefaultSelectionModel} this
32809         * @param {TreeNode} node the new selection
32810         */
32811        "selectionchange" : true,
32812
32813        /**
32814         * @event beforeselect
32815         * Fires before the selected node changes, return false to cancel the change
32816         * @param {DefaultSelectionModel} this
32817         * @param {TreeNode} node the new selection
32818         * @param {TreeNode} node the old selection
32819         */
32820        "beforeselect" : true
32821    });
32822    
32823     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32824 };
32825
32826 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32827     init : function(tree){
32828         this.tree = tree;
32829         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32830         tree.on("click", this.onNodeClick, this);
32831     },
32832     
32833     onNodeClick : function(node, e){
32834         if (e.ctrlKey && this.selNode == node)  {
32835             this.unselect(node);
32836             return;
32837         }
32838         this.select(node);
32839     },
32840     
32841     /**
32842      * Select a node.
32843      * @param {TreeNode} node The node to select
32844      * @return {TreeNode} The selected node
32845      */
32846     select : function(node){
32847         var last = this.selNode;
32848         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32849             if(last){
32850                 last.ui.onSelectedChange(false);
32851             }
32852             this.selNode = node;
32853             node.ui.onSelectedChange(true);
32854             this.fireEvent("selectionchange", this, node, last);
32855         }
32856         return node;
32857     },
32858     
32859     /**
32860      * Deselect a node.
32861      * @param {TreeNode} node The node to unselect
32862      */
32863     unselect : function(node){
32864         if(this.selNode == node){
32865             this.clearSelections();
32866         }    
32867     },
32868     
32869     /**
32870      * Clear all selections
32871      */
32872     clearSelections : function(){
32873         var n = this.selNode;
32874         if(n){
32875             n.ui.onSelectedChange(false);
32876             this.selNode = null;
32877             this.fireEvent("selectionchange", this, null);
32878         }
32879         return n;
32880     },
32881     
32882     /**
32883      * Get the selected node
32884      * @return {TreeNode} The selected node
32885      */
32886     getSelectedNode : function(){
32887         return this.selNode;    
32888     },
32889     
32890     /**
32891      * Returns true if the node is selected
32892      * @param {TreeNode} node The node to check
32893      * @return {Boolean}
32894      */
32895     isSelected : function(node){
32896         return this.selNode == node;  
32897     },
32898
32899     /**
32900      * Selects the node above the selected node in the tree, intelligently walking the nodes
32901      * @return TreeNode The new selection
32902      */
32903     selectPrevious : function(){
32904         var s = this.selNode || this.lastSelNode;
32905         if(!s){
32906             return null;
32907         }
32908         var ps = s.previousSibling;
32909         if(ps){
32910             if(!ps.isExpanded() || ps.childNodes.length < 1){
32911                 return this.select(ps);
32912             } else{
32913                 var lc = ps.lastChild;
32914                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32915                     lc = lc.lastChild;
32916                 }
32917                 return this.select(lc);
32918             }
32919         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32920             return this.select(s.parentNode);
32921         }
32922         return null;
32923     },
32924
32925     /**
32926      * Selects the node above the selected node in the tree, intelligently walking the nodes
32927      * @return TreeNode The new selection
32928      */
32929     selectNext : function(){
32930         var s = this.selNode || this.lastSelNode;
32931         if(!s){
32932             return null;
32933         }
32934         if(s.firstChild && s.isExpanded()){
32935              return this.select(s.firstChild);
32936          }else if(s.nextSibling){
32937              return this.select(s.nextSibling);
32938          }else if(s.parentNode){
32939             var newS = null;
32940             s.parentNode.bubble(function(){
32941                 if(this.nextSibling){
32942                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32943                     return false;
32944                 }
32945             });
32946             return newS;
32947          }
32948         return null;
32949     },
32950
32951     onKeyDown : function(e){
32952         var s = this.selNode || this.lastSelNode;
32953         // undesirable, but required
32954         var sm = this;
32955         if(!s){
32956             return;
32957         }
32958         var k = e.getKey();
32959         switch(k){
32960              case e.DOWN:
32961                  e.stopEvent();
32962                  this.selectNext();
32963              break;
32964              case e.UP:
32965                  e.stopEvent();
32966                  this.selectPrevious();
32967              break;
32968              case e.RIGHT:
32969                  e.preventDefault();
32970                  if(s.hasChildNodes()){
32971                      if(!s.isExpanded()){
32972                          s.expand();
32973                      }else if(s.firstChild){
32974                          this.select(s.firstChild, e);
32975                      }
32976                  }
32977              break;
32978              case e.LEFT:
32979                  e.preventDefault();
32980                  if(s.hasChildNodes() && s.isExpanded()){
32981                      s.collapse();
32982                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32983                      this.select(s.parentNode, e);
32984                  }
32985              break;
32986         };
32987     }
32988 });
32989
32990 /**
32991  * @class Roo.tree.MultiSelectionModel
32992  * @extends Roo.util.Observable
32993  * Multi selection for a TreePanel.
32994  * @param {Object} cfg Configuration
32995  */
32996 Roo.tree.MultiSelectionModel = function(){
32997    this.selNodes = [];
32998    this.selMap = {};
32999    this.addEvents({
33000        /**
33001         * @event selectionchange
33002         * Fires when the selected nodes change
33003         * @param {MultiSelectionModel} this
33004         * @param {Array} nodes Array of the selected nodes
33005         */
33006        "selectionchange" : true
33007    });
33008    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33009    
33010 };
33011
33012 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33013     init : function(tree){
33014         this.tree = tree;
33015         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33016         tree.on("click", this.onNodeClick, this);
33017     },
33018     
33019     onNodeClick : function(node, e){
33020         this.select(node, e, e.ctrlKey);
33021     },
33022     
33023     /**
33024      * Select a node.
33025      * @param {TreeNode} node The node to select
33026      * @param {EventObject} e (optional) An event associated with the selection
33027      * @param {Boolean} keepExisting True to retain existing selections
33028      * @return {TreeNode} The selected node
33029      */
33030     select : function(node, e, keepExisting){
33031         if(keepExisting !== true){
33032             this.clearSelections(true);
33033         }
33034         if(this.isSelected(node)){
33035             this.lastSelNode = node;
33036             return node;
33037         }
33038         this.selNodes.push(node);
33039         this.selMap[node.id] = node;
33040         this.lastSelNode = node;
33041         node.ui.onSelectedChange(true);
33042         this.fireEvent("selectionchange", this, this.selNodes);
33043         return node;
33044     },
33045     
33046     /**
33047      * Deselect a node.
33048      * @param {TreeNode} node The node to unselect
33049      */
33050     unselect : function(node){
33051         if(this.selMap[node.id]){
33052             node.ui.onSelectedChange(false);
33053             var sn = this.selNodes;
33054             var index = -1;
33055             if(sn.indexOf){
33056                 index = sn.indexOf(node);
33057             }else{
33058                 for(var i = 0, len = sn.length; i < len; i++){
33059                     if(sn[i] == node){
33060                         index = i;
33061                         break;
33062                     }
33063                 }
33064             }
33065             if(index != -1){
33066                 this.selNodes.splice(index, 1);
33067             }
33068             delete this.selMap[node.id];
33069             this.fireEvent("selectionchange", this, this.selNodes);
33070         }
33071     },
33072     
33073     /**
33074      * Clear all selections
33075      */
33076     clearSelections : function(suppressEvent){
33077         var sn = this.selNodes;
33078         if(sn.length > 0){
33079             for(var i = 0, len = sn.length; i < len; i++){
33080                 sn[i].ui.onSelectedChange(false);
33081             }
33082             this.selNodes = [];
33083             this.selMap = {};
33084             if(suppressEvent !== true){
33085                 this.fireEvent("selectionchange", this, this.selNodes);
33086             }
33087         }
33088     },
33089     
33090     /**
33091      * Returns true if the node is selected
33092      * @param {TreeNode} node The node to check
33093      * @return {Boolean}
33094      */
33095     isSelected : function(node){
33096         return this.selMap[node.id] ? true : false;  
33097     },
33098     
33099     /**
33100      * Returns an array of the selected nodes
33101      * @return {Array}
33102      */
33103     getSelectedNodes : function(){
33104         return this.selNodes;    
33105     },
33106
33107     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33108
33109     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33110
33111     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33112 });/*
33113  * Based on:
33114  * Ext JS Library 1.1.1
33115  * Copyright(c) 2006-2007, Ext JS, LLC.
33116  *
33117  * Originally Released Under LGPL - original licence link has changed is not relivant.
33118  *
33119  * Fork - LGPL
33120  * <script type="text/javascript">
33121  */
33122  
33123 /**
33124  * @class Roo.tree.TreeNode
33125  * @extends Roo.data.Node
33126  * @cfg {String} text The text for this node
33127  * @cfg {Boolean} expanded true to start the node expanded
33128  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33129  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33130  * @cfg {Boolean} disabled true to start the node disabled
33131  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33132  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33133  * @cfg {String} cls A css class to be added to the node
33134  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33135  * @cfg {String} href URL of the link used for the node (defaults to #)
33136  * @cfg {String} hrefTarget target frame for the link
33137  * @cfg {String} qtip An Ext QuickTip for the node
33138  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33139  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33140  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33141  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33142  * (defaults to undefined with no checkbox rendered)
33143  * @constructor
33144  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33145  */
33146 Roo.tree.TreeNode = function(attributes){
33147     attributes = attributes || {};
33148     if(typeof attributes == "string"){
33149         attributes = {text: attributes};
33150     }
33151     this.childrenRendered = false;
33152     this.rendered = false;
33153     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33154     this.expanded = attributes.expanded === true;
33155     this.isTarget = attributes.isTarget !== false;
33156     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33157     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33158
33159     /**
33160      * Read-only. The text for this node. To change it use setText().
33161      * @type String
33162      */
33163     this.text = attributes.text;
33164     /**
33165      * True if this node is disabled.
33166      * @type Boolean
33167      */
33168     this.disabled = attributes.disabled === true;
33169
33170     this.addEvents({
33171         /**
33172         * @event textchange
33173         * Fires when the text for this node is changed
33174         * @param {Node} this This node
33175         * @param {String} text The new text
33176         * @param {String} oldText The old text
33177         */
33178         "textchange" : true,
33179         /**
33180         * @event beforeexpand
33181         * Fires before this node is expanded, return false to cancel.
33182         * @param {Node} this This node
33183         * @param {Boolean} deep
33184         * @param {Boolean} anim
33185         */
33186         "beforeexpand" : true,
33187         /**
33188         * @event beforecollapse
33189         * Fires before this node is collapsed, return false to cancel.
33190         * @param {Node} this This node
33191         * @param {Boolean} deep
33192         * @param {Boolean} anim
33193         */
33194         "beforecollapse" : true,
33195         /**
33196         * @event expand
33197         * Fires when this node is expanded
33198         * @param {Node} this This node
33199         */
33200         "expand" : true,
33201         /**
33202         * @event disabledchange
33203         * Fires when the disabled status of this node changes
33204         * @param {Node} this This node
33205         * @param {Boolean} disabled
33206         */
33207         "disabledchange" : true,
33208         /**
33209         * @event collapse
33210         * Fires when this node is collapsed
33211         * @param {Node} this This node
33212         */
33213         "collapse" : true,
33214         /**
33215         * @event beforeclick
33216         * Fires before click processing. Return false to cancel the default action.
33217         * @param {Node} this This node
33218         * @param {Roo.EventObject} e The event object
33219         */
33220         "beforeclick":true,
33221         /**
33222         * @event checkchange
33223         * Fires when a node with a checkbox's checked property changes
33224         * @param {Node} this This node
33225         * @param {Boolean} checked
33226         */
33227         "checkchange":true,
33228         /**
33229         * @event click
33230         * Fires when this node is clicked
33231         * @param {Node} this This node
33232         * @param {Roo.EventObject} e The event object
33233         */
33234         "click":true,
33235         /**
33236         * @event dblclick
33237         * Fires when this node is double clicked
33238         * @param {Node} this This node
33239         * @param {Roo.EventObject} e The event object
33240         */
33241         "dblclick":true,
33242         /**
33243         * @event contextmenu
33244         * Fires when this node is right clicked
33245         * @param {Node} this This node
33246         * @param {Roo.EventObject} e The event object
33247         */
33248         "contextmenu":true,
33249         /**
33250         * @event beforechildrenrendered
33251         * Fires right before the child nodes for this node are rendered
33252         * @param {Node} this This node
33253         */
33254         "beforechildrenrendered":true
33255     });
33256
33257     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33258
33259     /**
33260      * Read-only. The UI for this node
33261      * @type TreeNodeUI
33262      */
33263     this.ui = new uiClass(this);
33264     
33265     // finally support items[]
33266     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33267         return;
33268     }
33269     
33270     
33271     Roo.each(this.attributes.items, function(c) {
33272         this.appendChild(Roo.factory(c,Roo.Tree));
33273     }, this);
33274     delete this.attributes.items;
33275     
33276     
33277     
33278 };
33279 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33280     preventHScroll: true,
33281     /**
33282      * Returns true if this node is expanded
33283      * @return {Boolean}
33284      */
33285     isExpanded : function(){
33286         return this.expanded;
33287     },
33288
33289     /**
33290      * Returns the UI object for this node
33291      * @return {TreeNodeUI}
33292      */
33293     getUI : function(){
33294         return this.ui;
33295     },
33296
33297     // private override
33298     setFirstChild : function(node){
33299         var of = this.firstChild;
33300         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33301         if(this.childrenRendered && of && node != of){
33302             of.renderIndent(true, true);
33303         }
33304         if(this.rendered){
33305             this.renderIndent(true, true);
33306         }
33307     },
33308
33309     // private override
33310     setLastChild : function(node){
33311         var ol = this.lastChild;
33312         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33313         if(this.childrenRendered && ol && node != ol){
33314             ol.renderIndent(true, true);
33315         }
33316         if(this.rendered){
33317             this.renderIndent(true, true);
33318         }
33319     },
33320
33321     // these methods are overridden to provide lazy rendering support
33322     // private override
33323     appendChild : function()
33324     {
33325         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33326         if(node && this.childrenRendered){
33327             node.render();
33328         }
33329         this.ui.updateExpandIcon();
33330         return node;
33331     },
33332
33333     // private override
33334     removeChild : function(node){
33335         this.ownerTree.getSelectionModel().unselect(node);
33336         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33337         // if it's been rendered remove dom node
33338         if(this.childrenRendered){
33339             node.ui.remove();
33340         }
33341         if(this.childNodes.length < 1){
33342             this.collapse(false, false);
33343         }else{
33344             this.ui.updateExpandIcon();
33345         }
33346         if(!this.firstChild) {
33347             this.childrenRendered = false;
33348         }
33349         return node;
33350     },
33351
33352     // private override
33353     insertBefore : function(node, refNode){
33354         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33355         if(newNode && refNode && this.childrenRendered){
33356             node.render();
33357         }
33358         this.ui.updateExpandIcon();
33359         return newNode;
33360     },
33361
33362     /**
33363      * Sets the text for this node
33364      * @param {String} text
33365      */
33366     setText : function(text){
33367         var oldText = this.text;
33368         this.text = text;
33369         this.attributes.text = text;
33370         if(this.rendered){ // event without subscribing
33371             this.ui.onTextChange(this, text, oldText);
33372         }
33373         this.fireEvent("textchange", this, text, oldText);
33374     },
33375
33376     /**
33377      * Triggers selection of this node
33378      */
33379     select : function(){
33380         this.getOwnerTree().getSelectionModel().select(this);
33381     },
33382
33383     /**
33384      * Triggers deselection of this node
33385      */
33386     unselect : function(){
33387         this.getOwnerTree().getSelectionModel().unselect(this);
33388     },
33389
33390     /**
33391      * Returns true if this node is selected
33392      * @return {Boolean}
33393      */
33394     isSelected : function(){
33395         return this.getOwnerTree().getSelectionModel().isSelected(this);
33396     },
33397
33398     /**
33399      * Expand this node.
33400      * @param {Boolean} deep (optional) True to expand all children as well
33401      * @param {Boolean} anim (optional) false to cancel the default animation
33402      * @param {Function} callback (optional) A callback to be called when
33403      * expanding this node completes (does not wait for deep expand to complete).
33404      * Called with 1 parameter, this node.
33405      */
33406     expand : function(deep, anim, callback){
33407         if(!this.expanded){
33408             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33409                 return;
33410             }
33411             if(!this.childrenRendered){
33412                 this.renderChildren();
33413             }
33414             this.expanded = true;
33415             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33416                 this.ui.animExpand(function(){
33417                     this.fireEvent("expand", this);
33418                     if(typeof callback == "function"){
33419                         callback(this);
33420                     }
33421                     if(deep === true){
33422                         this.expandChildNodes(true);
33423                     }
33424                 }.createDelegate(this));
33425                 return;
33426             }else{
33427                 this.ui.expand();
33428                 this.fireEvent("expand", this);
33429                 if(typeof callback == "function"){
33430                     callback(this);
33431                 }
33432             }
33433         }else{
33434            if(typeof callback == "function"){
33435                callback(this);
33436            }
33437         }
33438         if(deep === true){
33439             this.expandChildNodes(true);
33440         }
33441     },
33442
33443     isHiddenRoot : function(){
33444         return this.isRoot && !this.getOwnerTree().rootVisible;
33445     },
33446
33447     /**
33448      * Collapse this node.
33449      * @param {Boolean} deep (optional) True to collapse all children as well
33450      * @param {Boolean} anim (optional) false to cancel the default animation
33451      */
33452     collapse : function(deep, anim){
33453         if(this.expanded && !this.isHiddenRoot()){
33454             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33455                 return;
33456             }
33457             this.expanded = false;
33458             if((this.getOwnerTree().animate && anim !== false) || anim){
33459                 this.ui.animCollapse(function(){
33460                     this.fireEvent("collapse", this);
33461                     if(deep === true){
33462                         this.collapseChildNodes(true);
33463                     }
33464                 }.createDelegate(this));
33465                 return;
33466             }else{
33467                 this.ui.collapse();
33468                 this.fireEvent("collapse", this);
33469             }
33470         }
33471         if(deep === true){
33472             var cs = this.childNodes;
33473             for(var i = 0, len = cs.length; i < len; i++) {
33474                 cs[i].collapse(true, false);
33475             }
33476         }
33477     },
33478
33479     // private
33480     delayedExpand : function(delay){
33481         if(!this.expandProcId){
33482             this.expandProcId = this.expand.defer(delay, this);
33483         }
33484     },
33485
33486     // private
33487     cancelExpand : function(){
33488         if(this.expandProcId){
33489             clearTimeout(this.expandProcId);
33490         }
33491         this.expandProcId = false;
33492     },
33493
33494     /**
33495      * Toggles expanded/collapsed state of the node
33496      */
33497     toggle : function(){
33498         if(this.expanded){
33499             this.collapse();
33500         }else{
33501             this.expand();
33502         }
33503     },
33504
33505     /**
33506      * Ensures all parent nodes are expanded
33507      */
33508     ensureVisible : function(callback){
33509         var tree = this.getOwnerTree();
33510         tree.expandPath(this.parentNode.getPath(), false, function(){
33511             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33512             Roo.callback(callback);
33513         }.createDelegate(this));
33514     },
33515
33516     /**
33517      * Expand all child nodes
33518      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33519      */
33520     expandChildNodes : function(deep){
33521         var cs = this.childNodes;
33522         for(var i = 0, len = cs.length; i < len; i++) {
33523                 cs[i].expand(deep);
33524         }
33525     },
33526
33527     /**
33528      * Collapse all child nodes
33529      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33530      */
33531     collapseChildNodes : function(deep){
33532         var cs = this.childNodes;
33533         for(var i = 0, len = cs.length; i < len; i++) {
33534                 cs[i].collapse(deep);
33535         }
33536     },
33537
33538     /**
33539      * Disables this node
33540      */
33541     disable : function(){
33542         this.disabled = true;
33543         this.unselect();
33544         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33545             this.ui.onDisableChange(this, true);
33546         }
33547         this.fireEvent("disabledchange", this, true);
33548     },
33549
33550     /**
33551      * Enables this node
33552      */
33553     enable : function(){
33554         this.disabled = false;
33555         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33556             this.ui.onDisableChange(this, false);
33557         }
33558         this.fireEvent("disabledchange", this, false);
33559     },
33560
33561     // private
33562     renderChildren : function(suppressEvent){
33563         if(suppressEvent !== false){
33564             this.fireEvent("beforechildrenrendered", this);
33565         }
33566         var cs = this.childNodes;
33567         for(var i = 0, len = cs.length; i < len; i++){
33568             cs[i].render(true);
33569         }
33570         this.childrenRendered = true;
33571     },
33572
33573     // private
33574     sort : function(fn, scope){
33575         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33576         if(this.childrenRendered){
33577             var cs = this.childNodes;
33578             for(var i = 0, len = cs.length; i < len; i++){
33579                 cs[i].render(true);
33580             }
33581         }
33582     },
33583
33584     // private
33585     render : function(bulkRender){
33586         this.ui.render(bulkRender);
33587         if(!this.rendered){
33588             this.rendered = true;
33589             if(this.expanded){
33590                 this.expanded = false;
33591                 this.expand(false, false);
33592             }
33593         }
33594     },
33595
33596     // private
33597     renderIndent : function(deep, refresh){
33598         if(refresh){
33599             this.ui.childIndent = null;
33600         }
33601         this.ui.renderIndent();
33602         if(deep === true && this.childrenRendered){
33603             var cs = this.childNodes;
33604             for(var i = 0, len = cs.length; i < len; i++){
33605                 cs[i].renderIndent(true, refresh);
33606             }
33607         }
33608     }
33609 });/*
33610  * Based on:
33611  * Ext JS Library 1.1.1
33612  * Copyright(c) 2006-2007, Ext JS, LLC.
33613  *
33614  * Originally Released Under LGPL - original licence link has changed is not relivant.
33615  *
33616  * Fork - LGPL
33617  * <script type="text/javascript">
33618  */
33619  
33620 /**
33621  * @class Roo.tree.AsyncTreeNode
33622  * @extends Roo.tree.TreeNode
33623  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33624  * @constructor
33625  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33626  */
33627  Roo.tree.AsyncTreeNode = function(config){
33628     this.loaded = false;
33629     this.loading = false;
33630     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33631     /**
33632     * @event beforeload
33633     * Fires before this node is loaded, return false to cancel
33634     * @param {Node} this This node
33635     */
33636     this.addEvents({'beforeload':true, 'load': true});
33637     /**
33638     * @event load
33639     * Fires when this node is loaded
33640     * @param {Node} this This node
33641     */
33642     /**
33643      * The loader used by this node (defaults to using the tree's defined loader)
33644      * @type TreeLoader
33645      * @property loader
33646      */
33647 };
33648 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33649     expand : function(deep, anim, callback){
33650         if(this.loading){ // if an async load is already running, waiting til it's done
33651             var timer;
33652             var f = function(){
33653                 if(!this.loading){ // done loading
33654                     clearInterval(timer);
33655                     this.expand(deep, anim, callback);
33656                 }
33657             }.createDelegate(this);
33658             timer = setInterval(f, 200);
33659             return;
33660         }
33661         if(!this.loaded){
33662             if(this.fireEvent("beforeload", this) === false){
33663                 return;
33664             }
33665             this.loading = true;
33666             this.ui.beforeLoad(this);
33667             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33668             if(loader){
33669                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33670                 return;
33671             }
33672         }
33673         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33674     },
33675     
33676     /**
33677      * Returns true if this node is currently loading
33678      * @return {Boolean}
33679      */
33680     isLoading : function(){
33681         return this.loading;  
33682     },
33683     
33684     loadComplete : function(deep, anim, callback){
33685         this.loading = false;
33686         this.loaded = true;
33687         this.ui.afterLoad(this);
33688         this.fireEvent("load", this);
33689         this.expand(deep, anim, callback);
33690     },
33691     
33692     /**
33693      * Returns true if this node has been loaded
33694      * @return {Boolean}
33695      */
33696     isLoaded : function(){
33697         return this.loaded;
33698     },
33699     
33700     hasChildNodes : function(){
33701         if(!this.isLeaf() && !this.loaded){
33702             return true;
33703         }else{
33704             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33705         }
33706     },
33707
33708     /**
33709      * Trigger a reload for this node
33710      * @param {Function} callback
33711      */
33712     reload : function(callback){
33713         this.collapse(false, false);
33714         while(this.firstChild){
33715             this.removeChild(this.firstChild);
33716         }
33717         this.childrenRendered = false;
33718         this.loaded = false;
33719         if(this.isHiddenRoot()){
33720             this.expanded = false;
33721         }
33722         this.expand(false, false, callback);
33723     }
33724 });/*
33725  * Based on:
33726  * Ext JS Library 1.1.1
33727  * Copyright(c) 2006-2007, Ext JS, LLC.
33728  *
33729  * Originally Released Under LGPL - original licence link has changed is not relivant.
33730  *
33731  * Fork - LGPL
33732  * <script type="text/javascript">
33733  */
33734  
33735 /**
33736  * @class Roo.tree.TreeNodeUI
33737  * @constructor
33738  * @param {Object} node The node to render
33739  * The TreeNode UI implementation is separate from the
33740  * tree implementation. Unless you are customizing the tree UI,
33741  * you should never have to use this directly.
33742  */
33743 Roo.tree.TreeNodeUI = function(node){
33744     this.node = node;
33745     this.rendered = false;
33746     this.animating = false;
33747     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33748 };
33749
33750 Roo.tree.TreeNodeUI.prototype = {
33751     removeChild : function(node){
33752         if(this.rendered){
33753             this.ctNode.removeChild(node.ui.getEl());
33754         }
33755     },
33756
33757     beforeLoad : function(){
33758          this.addClass("x-tree-node-loading");
33759     },
33760
33761     afterLoad : function(){
33762          this.removeClass("x-tree-node-loading");
33763     },
33764
33765     onTextChange : function(node, text, oldText){
33766         if(this.rendered){
33767             this.textNode.innerHTML = text;
33768         }
33769     },
33770
33771     onDisableChange : function(node, state){
33772         this.disabled = state;
33773         if(state){
33774             this.addClass("x-tree-node-disabled");
33775         }else{
33776             this.removeClass("x-tree-node-disabled");
33777         }
33778     },
33779
33780     onSelectedChange : function(state){
33781         if(state){
33782             this.focus();
33783             this.addClass("x-tree-selected");
33784         }else{
33785             //this.blur();
33786             this.removeClass("x-tree-selected");
33787         }
33788     },
33789
33790     onMove : function(tree, node, oldParent, newParent, index, refNode){
33791         this.childIndent = null;
33792         if(this.rendered){
33793             var targetNode = newParent.ui.getContainer();
33794             if(!targetNode){//target not rendered
33795                 this.holder = document.createElement("div");
33796                 this.holder.appendChild(this.wrap);
33797                 return;
33798             }
33799             var insertBefore = refNode ? refNode.ui.getEl() : null;
33800             if(insertBefore){
33801                 targetNode.insertBefore(this.wrap, insertBefore);
33802             }else{
33803                 targetNode.appendChild(this.wrap);
33804             }
33805             this.node.renderIndent(true);
33806         }
33807     },
33808
33809     addClass : function(cls){
33810         if(this.elNode){
33811             Roo.fly(this.elNode).addClass(cls);
33812         }
33813     },
33814
33815     removeClass : function(cls){
33816         if(this.elNode){
33817             Roo.fly(this.elNode).removeClass(cls);
33818         }
33819     },
33820
33821     remove : function(){
33822         if(this.rendered){
33823             this.holder = document.createElement("div");
33824             this.holder.appendChild(this.wrap);
33825         }
33826     },
33827
33828     fireEvent : function(){
33829         return this.node.fireEvent.apply(this.node, arguments);
33830     },
33831
33832     initEvents : function(){
33833         this.node.on("move", this.onMove, this);
33834         var E = Roo.EventManager;
33835         var a = this.anchor;
33836
33837         var el = Roo.fly(a, '_treeui');
33838
33839         if(Roo.isOpera){ // opera render bug ignores the CSS
33840             el.setStyle("text-decoration", "none");
33841         }
33842
33843         el.on("click", this.onClick, this);
33844         el.on("dblclick", this.onDblClick, this);
33845
33846         if(this.checkbox){
33847             Roo.EventManager.on(this.checkbox,
33848                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33849         }
33850
33851         el.on("contextmenu", this.onContextMenu, this);
33852
33853         var icon = Roo.fly(this.iconNode);
33854         icon.on("click", this.onClick, this);
33855         icon.on("dblclick", this.onDblClick, this);
33856         icon.on("contextmenu", this.onContextMenu, this);
33857         E.on(this.ecNode, "click", this.ecClick, this, true);
33858
33859         if(this.node.disabled){
33860             this.addClass("x-tree-node-disabled");
33861         }
33862         if(this.node.hidden){
33863             this.addClass("x-tree-node-disabled");
33864         }
33865         var ot = this.node.getOwnerTree();
33866         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33867         if(dd && (!this.node.isRoot || ot.rootVisible)){
33868             Roo.dd.Registry.register(this.elNode, {
33869                 node: this.node,
33870                 handles: this.getDDHandles(),
33871                 isHandle: false
33872             });
33873         }
33874     },
33875
33876     getDDHandles : function(){
33877         return [this.iconNode, this.textNode];
33878     },
33879
33880     hide : function(){
33881         if(this.rendered){
33882             this.wrap.style.display = "none";
33883         }
33884     },
33885
33886     show : function(){
33887         if(this.rendered){
33888             this.wrap.style.display = "";
33889         }
33890     },
33891
33892     onContextMenu : function(e){
33893         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33894             e.preventDefault();
33895             this.focus();
33896             this.fireEvent("contextmenu", this.node, e);
33897         }
33898     },
33899
33900     onClick : function(e){
33901         if(this.dropping){
33902             e.stopEvent();
33903             return;
33904         }
33905         if(this.fireEvent("beforeclick", this.node, e) !== false){
33906             if(!this.disabled && this.node.attributes.href){
33907                 this.fireEvent("click", this.node, e);
33908                 return;
33909             }
33910             e.preventDefault();
33911             if(this.disabled){
33912                 return;
33913             }
33914
33915             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33916                 this.node.toggle();
33917             }
33918
33919             this.fireEvent("click", this.node, e);
33920         }else{
33921             e.stopEvent();
33922         }
33923     },
33924
33925     onDblClick : function(e){
33926         e.preventDefault();
33927         if(this.disabled){
33928             return;
33929         }
33930         if(this.checkbox){
33931             this.toggleCheck();
33932         }
33933         if(!this.animating && this.node.hasChildNodes()){
33934             this.node.toggle();
33935         }
33936         this.fireEvent("dblclick", this.node, e);
33937     },
33938
33939     onCheckChange : function(){
33940         var checked = this.checkbox.checked;
33941         this.node.attributes.checked = checked;
33942         this.fireEvent('checkchange', this.node, checked);
33943     },
33944
33945     ecClick : function(e){
33946         if(!this.animating && this.node.hasChildNodes()){
33947             this.node.toggle();
33948         }
33949     },
33950
33951     startDrop : function(){
33952         this.dropping = true;
33953     },
33954
33955     // delayed drop so the click event doesn't get fired on a drop
33956     endDrop : function(){
33957        setTimeout(function(){
33958            this.dropping = false;
33959        }.createDelegate(this), 50);
33960     },
33961
33962     expand : function(){
33963         this.updateExpandIcon();
33964         this.ctNode.style.display = "";
33965     },
33966
33967     focus : function(){
33968         if(!this.node.preventHScroll){
33969             try{this.anchor.focus();
33970             }catch(e){}
33971         }else if(!Roo.isIE){
33972             try{
33973                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33974                 var l = noscroll.scrollLeft;
33975                 this.anchor.focus();
33976                 noscroll.scrollLeft = l;
33977             }catch(e){}
33978         }
33979     },
33980
33981     toggleCheck : function(value){
33982         var cb = this.checkbox;
33983         if(cb){
33984             cb.checked = (value === undefined ? !cb.checked : value);
33985         }
33986     },
33987
33988     blur : function(){
33989         try{
33990             this.anchor.blur();
33991         }catch(e){}
33992     },
33993
33994     animExpand : function(callback){
33995         var ct = Roo.get(this.ctNode);
33996         ct.stopFx();
33997         if(!this.node.hasChildNodes()){
33998             this.updateExpandIcon();
33999             this.ctNode.style.display = "";
34000             Roo.callback(callback);
34001             return;
34002         }
34003         this.animating = true;
34004         this.updateExpandIcon();
34005
34006         ct.slideIn('t', {
34007            callback : function(){
34008                this.animating = false;
34009                Roo.callback(callback);
34010             },
34011             scope: this,
34012             duration: this.node.ownerTree.duration || .25
34013         });
34014     },
34015
34016     highlight : function(){
34017         var tree = this.node.getOwnerTree();
34018         Roo.fly(this.wrap).highlight(
34019             tree.hlColor || "C3DAF9",
34020             {endColor: tree.hlBaseColor}
34021         );
34022     },
34023
34024     collapse : function(){
34025         this.updateExpandIcon();
34026         this.ctNode.style.display = "none";
34027     },
34028
34029     animCollapse : function(callback){
34030         var ct = Roo.get(this.ctNode);
34031         ct.enableDisplayMode('block');
34032         ct.stopFx();
34033
34034         this.animating = true;
34035         this.updateExpandIcon();
34036
34037         ct.slideOut('t', {
34038             callback : function(){
34039                this.animating = false;
34040                Roo.callback(callback);
34041             },
34042             scope: this,
34043             duration: this.node.ownerTree.duration || .25
34044         });
34045     },
34046
34047     getContainer : function(){
34048         return this.ctNode;
34049     },
34050
34051     getEl : function(){
34052         return this.wrap;
34053     },
34054
34055     appendDDGhost : function(ghostNode){
34056         ghostNode.appendChild(this.elNode.cloneNode(true));
34057     },
34058
34059     getDDRepairXY : function(){
34060         return Roo.lib.Dom.getXY(this.iconNode);
34061     },
34062
34063     onRender : function(){
34064         this.render();
34065     },
34066
34067     render : function(bulkRender){
34068         var n = this.node, a = n.attributes;
34069         var targetNode = n.parentNode ?
34070               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34071
34072         if(!this.rendered){
34073             this.rendered = true;
34074
34075             this.renderElements(n, a, targetNode, bulkRender);
34076
34077             if(a.qtip){
34078                if(this.textNode.setAttributeNS){
34079                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34080                    if(a.qtipTitle){
34081                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34082                    }
34083                }else{
34084                    this.textNode.setAttribute("ext:qtip", a.qtip);
34085                    if(a.qtipTitle){
34086                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34087                    }
34088                }
34089             }else if(a.qtipCfg){
34090                 a.qtipCfg.target = Roo.id(this.textNode);
34091                 Roo.QuickTips.register(a.qtipCfg);
34092             }
34093             this.initEvents();
34094             if(!this.node.expanded){
34095                 this.updateExpandIcon();
34096             }
34097         }else{
34098             if(bulkRender === true) {
34099                 targetNode.appendChild(this.wrap);
34100             }
34101         }
34102     },
34103
34104     renderElements : function(n, a, targetNode, bulkRender)
34105     {
34106         // add some indent caching, this helps performance when rendering a large tree
34107         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34108         var t = n.getOwnerTree();
34109         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34110         if (typeof(n.attributes.html) != 'undefined') {
34111             txt = n.attributes.html;
34112         }
34113         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34114         var cb = typeof a.checked == 'boolean';
34115         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34116         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34117             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34118             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34119             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34120             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34121             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34122              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34123                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34124             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34125             "</li>"];
34126
34127         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34128             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34129                                 n.nextSibling.ui.getEl(), buf.join(""));
34130         }else{
34131             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34132         }
34133
34134         this.elNode = this.wrap.childNodes[0];
34135         this.ctNode = this.wrap.childNodes[1];
34136         var cs = this.elNode.childNodes;
34137         this.indentNode = cs[0];
34138         this.ecNode = cs[1];
34139         this.iconNode = cs[2];
34140         var index = 3;
34141         if(cb){
34142             this.checkbox = cs[3];
34143             index++;
34144         }
34145         this.anchor = cs[index];
34146         this.textNode = cs[index].firstChild;
34147     },
34148
34149     getAnchor : function(){
34150         return this.anchor;
34151     },
34152
34153     getTextEl : function(){
34154         return this.textNode;
34155     },
34156
34157     getIconEl : function(){
34158         return this.iconNode;
34159     },
34160
34161     isChecked : function(){
34162         return this.checkbox ? this.checkbox.checked : false;
34163     },
34164
34165     updateExpandIcon : function(){
34166         if(this.rendered){
34167             var n = this.node, c1, c2;
34168             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34169             var hasChild = n.hasChildNodes();
34170             if(hasChild){
34171                 if(n.expanded){
34172                     cls += "-minus";
34173                     c1 = "x-tree-node-collapsed";
34174                     c2 = "x-tree-node-expanded";
34175                 }else{
34176                     cls += "-plus";
34177                     c1 = "x-tree-node-expanded";
34178                     c2 = "x-tree-node-collapsed";
34179                 }
34180                 if(this.wasLeaf){
34181                     this.removeClass("x-tree-node-leaf");
34182                     this.wasLeaf = false;
34183                 }
34184                 if(this.c1 != c1 || this.c2 != c2){
34185                     Roo.fly(this.elNode).replaceClass(c1, c2);
34186                     this.c1 = c1; this.c2 = c2;
34187                 }
34188             }else{
34189                 // this changes non-leafs into leafs if they have no children.
34190                 // it's not very rational behaviour..
34191                 
34192                 if(!this.wasLeaf && this.node.leaf){
34193                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34194                     delete this.c1;
34195                     delete this.c2;
34196                     this.wasLeaf = true;
34197                 }
34198             }
34199             var ecc = "x-tree-ec-icon "+cls;
34200             if(this.ecc != ecc){
34201                 this.ecNode.className = ecc;
34202                 this.ecc = ecc;
34203             }
34204         }
34205     },
34206
34207     getChildIndent : function(){
34208         if(!this.childIndent){
34209             var buf = [];
34210             var p = this.node;
34211             while(p){
34212                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34213                     if(!p.isLast()) {
34214                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34215                     } else {
34216                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34217                     }
34218                 }
34219                 p = p.parentNode;
34220             }
34221             this.childIndent = buf.join("");
34222         }
34223         return this.childIndent;
34224     },
34225
34226     renderIndent : function(){
34227         if(this.rendered){
34228             var indent = "";
34229             var p = this.node.parentNode;
34230             if(p){
34231                 indent = p.ui.getChildIndent();
34232             }
34233             if(this.indentMarkup != indent){ // don't rerender if not required
34234                 this.indentNode.innerHTML = indent;
34235                 this.indentMarkup = indent;
34236             }
34237             this.updateExpandIcon();
34238         }
34239     }
34240 };
34241
34242 Roo.tree.RootTreeNodeUI = function(){
34243     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34244 };
34245 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34246     render : function(){
34247         if(!this.rendered){
34248             var targetNode = this.node.ownerTree.innerCt.dom;
34249             this.node.expanded = true;
34250             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34251             this.wrap = this.ctNode = targetNode.firstChild;
34252         }
34253     },
34254     collapse : function(){
34255     },
34256     expand : function(){
34257     }
34258 });/*
34259  * Based on:
34260  * Ext JS Library 1.1.1
34261  * Copyright(c) 2006-2007, Ext JS, LLC.
34262  *
34263  * Originally Released Under LGPL - original licence link has changed is not relivant.
34264  *
34265  * Fork - LGPL
34266  * <script type="text/javascript">
34267  */
34268 /**
34269  * @class Roo.tree.TreeLoader
34270  * @extends Roo.util.Observable
34271  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34272  * nodes from a specified URL. The response must be a javascript Array definition
34273  * who's elements are node definition objects. eg:
34274  * <pre><code>
34275 {  success : true,
34276    data :      [
34277    
34278     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34279     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34280     ]
34281 }
34282
34283
34284 </code></pre>
34285  * <br><br>
34286  * The old style respose with just an array is still supported, but not recommended.
34287  * <br><br>
34288  *
34289  * A server request is sent, and child nodes are loaded only when a node is expanded.
34290  * The loading node's id is passed to the server under the parameter name "node" to
34291  * enable the server to produce the correct child nodes.
34292  * <br><br>
34293  * To pass extra parameters, an event handler may be attached to the "beforeload"
34294  * event, and the parameters specified in the TreeLoader's baseParams property:
34295  * <pre><code>
34296     myTreeLoader.on("beforeload", function(treeLoader, node) {
34297         this.baseParams.category = node.attributes.category;
34298     }, this);
34299 </code></pre><
34300  * This would pass an HTTP parameter called "category" to the server containing
34301  * the value of the Node's "category" attribute.
34302  * @constructor
34303  * Creates a new Treeloader.
34304  * @param {Object} config A config object containing config properties.
34305  */
34306 Roo.tree.TreeLoader = function(config){
34307     this.baseParams = {};
34308     this.requestMethod = "POST";
34309     Roo.apply(this, config);
34310
34311     this.addEvents({
34312     
34313         /**
34314          * @event beforeload
34315          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34316          * @param {Object} This TreeLoader object.
34317          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34318          * @param {Object} callback The callback function specified in the {@link #load} call.
34319          */
34320         beforeload : true,
34321         /**
34322          * @event load
34323          * Fires when the node has been successfuly loaded.
34324          * @param {Object} This TreeLoader object.
34325          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34326          * @param {Object} response The response object containing the data from the server.
34327          */
34328         load : true,
34329         /**
34330          * @event loadexception
34331          * Fires if the network request failed.
34332          * @param {Object} This TreeLoader object.
34333          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34334          * @param {Object} response The response object containing the data from the server.
34335          */
34336         loadexception : true,
34337         /**
34338          * @event create
34339          * Fires before a node is created, enabling you to return custom Node types 
34340          * @param {Object} This TreeLoader object.
34341          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34342          */
34343         create : true
34344     });
34345
34346     Roo.tree.TreeLoader.superclass.constructor.call(this);
34347 };
34348
34349 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34350     /**
34351     * @cfg {String} dataUrl The URL from which to request a Json string which
34352     * specifies an array of node definition object representing the child nodes
34353     * to be loaded.
34354     */
34355     /**
34356     * @cfg {String} requestMethod either GET or POST
34357     * defaults to POST (due to BC)
34358     * to be loaded.
34359     */
34360     /**
34361     * @cfg {Object} baseParams (optional) An object containing properties which
34362     * specify HTTP parameters to be passed to each request for child nodes.
34363     */
34364     /**
34365     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34366     * created by this loader. If the attributes sent by the server have an attribute in this object,
34367     * they take priority.
34368     */
34369     /**
34370     * @cfg {Object} uiProviders (optional) An object containing properties which
34371     * 
34372     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34373     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34374     * <i>uiProvider</i> attribute of a returned child node is a string rather
34375     * than a reference to a TreeNodeUI implementation, this that string value
34376     * is used as a property name in the uiProviders object. You can define the provider named
34377     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34378     */
34379     uiProviders : {},
34380
34381     /**
34382     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34383     * child nodes before loading.
34384     */
34385     clearOnLoad : true,
34386
34387     /**
34388     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34389     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34390     * Grid query { data : [ .....] }
34391     */
34392     
34393     root : false,
34394      /**
34395     * @cfg {String} queryParam (optional) 
34396     * Name of the query as it will be passed on the querystring (defaults to 'node')
34397     * eg. the request will be ?node=[id]
34398     */
34399     
34400     
34401     queryParam: false,
34402     
34403     /**
34404      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34405      * This is called automatically when a node is expanded, but may be used to reload
34406      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34407      * @param {Roo.tree.TreeNode} node
34408      * @param {Function} callback
34409      */
34410     load : function(node, callback){
34411         if(this.clearOnLoad){
34412             while(node.firstChild){
34413                 node.removeChild(node.firstChild);
34414             }
34415         }
34416         if(node.attributes.children){ // preloaded json children
34417             var cs = node.attributes.children;
34418             for(var i = 0, len = cs.length; i < len; i++){
34419                 node.appendChild(this.createNode(cs[i]));
34420             }
34421             if(typeof callback == "function"){
34422                 callback();
34423             }
34424         }else if(this.dataUrl){
34425             this.requestData(node, callback);
34426         }
34427     },
34428
34429     getParams: function(node){
34430         var buf = [], bp = this.baseParams;
34431         for(var key in bp){
34432             if(typeof bp[key] != "function"){
34433                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34434             }
34435         }
34436         var n = this.queryParam === false ? 'node' : this.queryParam;
34437         buf.push(n + "=", encodeURIComponent(node.id));
34438         return buf.join("");
34439     },
34440
34441     requestData : function(node, callback){
34442         if(this.fireEvent("beforeload", this, node, callback) !== false){
34443             this.transId = Roo.Ajax.request({
34444                 method:this.requestMethod,
34445                 url: this.dataUrl||this.url,
34446                 success: this.handleResponse,
34447                 failure: this.handleFailure,
34448                 scope: this,
34449                 argument: {callback: callback, node: node},
34450                 params: this.getParams(node)
34451             });
34452         }else{
34453             // if the load is cancelled, make sure we notify
34454             // the node that we are done
34455             if(typeof callback == "function"){
34456                 callback();
34457             }
34458         }
34459     },
34460
34461     isLoading : function(){
34462         return this.transId ? true : false;
34463     },
34464
34465     abort : function(){
34466         if(this.isLoading()){
34467             Roo.Ajax.abort(this.transId);
34468         }
34469     },
34470
34471     // private
34472     createNode : function(attr)
34473     {
34474         // apply baseAttrs, nice idea Corey!
34475         if(this.baseAttrs){
34476             Roo.applyIf(attr, this.baseAttrs);
34477         }
34478         if(this.applyLoader !== false){
34479             attr.loader = this;
34480         }
34481         // uiProvider = depreciated..
34482         
34483         if(typeof(attr.uiProvider) == 'string'){
34484            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34485                 /**  eval:var:attr */ eval(attr.uiProvider);
34486         }
34487         if(typeof(this.uiProviders['default']) != 'undefined') {
34488             attr.uiProvider = this.uiProviders['default'];
34489         }
34490         
34491         this.fireEvent('create', this, attr);
34492         
34493         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34494         return(attr.leaf ?
34495                         new Roo.tree.TreeNode(attr) :
34496                         new Roo.tree.AsyncTreeNode(attr));
34497     },
34498
34499     processResponse : function(response, node, callback)
34500     {
34501         var json = response.responseText;
34502         try {
34503             
34504             var o = Roo.decode(json);
34505             
34506             if (this.root === false && typeof(o.success) != undefined) {
34507                 this.root = 'data'; // the default behaviour for list like data..
34508                 }
34509                 
34510             if (this.root !== false &&  !o.success) {
34511                 // it's a failure condition.
34512                 var a = response.argument;
34513                 this.fireEvent("loadexception", this, a.node, response);
34514                 Roo.log("Load failed - should have a handler really");
34515                 return;
34516             }
34517             
34518             
34519             
34520             if (this.root !== false) {
34521                  o = o[this.root];
34522             }
34523             
34524             for(var i = 0, len = o.length; i < len; i++){
34525                 var n = this.createNode(o[i]);
34526                 if(n){
34527                     node.appendChild(n);
34528                 }
34529             }
34530             if(typeof callback == "function"){
34531                 callback(this, node);
34532             }
34533         }catch(e){
34534             this.handleFailure(response);
34535         }
34536     },
34537
34538     handleResponse : function(response){
34539         this.transId = false;
34540         var a = response.argument;
34541         this.processResponse(response, a.node, a.callback);
34542         this.fireEvent("load", this, a.node, response);
34543     },
34544
34545     handleFailure : function(response)
34546     {
34547         // should handle failure better..
34548         this.transId = false;
34549         var a = response.argument;
34550         this.fireEvent("loadexception", this, a.node, response);
34551         if(typeof a.callback == "function"){
34552             a.callback(this, a.node);
34553         }
34554     }
34555 });/*
34556  * Based on:
34557  * Ext JS Library 1.1.1
34558  * Copyright(c) 2006-2007, Ext JS, LLC.
34559  *
34560  * Originally Released Under LGPL - original licence link has changed is not relivant.
34561  *
34562  * Fork - LGPL
34563  * <script type="text/javascript">
34564  */
34565
34566 /**
34567 * @class Roo.tree.TreeFilter
34568 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34569 * @param {TreePanel} tree
34570 * @param {Object} config (optional)
34571  */
34572 Roo.tree.TreeFilter = function(tree, config){
34573     this.tree = tree;
34574     this.filtered = {};
34575     Roo.apply(this, config);
34576 };
34577
34578 Roo.tree.TreeFilter.prototype = {
34579     clearBlank:false,
34580     reverse:false,
34581     autoClear:false,
34582     remove:false,
34583
34584      /**
34585      * Filter the data by a specific attribute.
34586      * @param {String/RegExp} value Either string that the attribute value
34587      * should start with or a RegExp to test against the attribute
34588      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34589      * @param {TreeNode} startNode (optional) The node to start the filter at.
34590      */
34591     filter : function(value, attr, startNode){
34592         attr = attr || "text";
34593         var f;
34594         if(typeof value == "string"){
34595             var vlen = value.length;
34596             // auto clear empty filter
34597             if(vlen == 0 && this.clearBlank){
34598                 this.clear();
34599                 return;
34600             }
34601             value = value.toLowerCase();
34602             f = function(n){
34603                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34604             };
34605         }else if(value.exec){ // regex?
34606             f = function(n){
34607                 return value.test(n.attributes[attr]);
34608             };
34609         }else{
34610             throw 'Illegal filter type, must be string or regex';
34611         }
34612         this.filterBy(f, null, startNode);
34613         },
34614
34615     /**
34616      * Filter by a function. The passed function will be called with each
34617      * node in the tree (or from the startNode). If the function returns true, the node is kept
34618      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34619      * @param {Function} fn The filter function
34620      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34621      */
34622     filterBy : function(fn, scope, startNode){
34623         startNode = startNode || this.tree.root;
34624         if(this.autoClear){
34625             this.clear();
34626         }
34627         var af = this.filtered, rv = this.reverse;
34628         var f = function(n){
34629             if(n == startNode){
34630                 return true;
34631             }
34632             if(af[n.id]){
34633                 return false;
34634             }
34635             var m = fn.call(scope || n, n);
34636             if(!m || rv){
34637                 af[n.id] = n;
34638                 n.ui.hide();
34639                 return false;
34640             }
34641             return true;
34642         };
34643         startNode.cascade(f);
34644         if(this.remove){
34645            for(var id in af){
34646                if(typeof id != "function"){
34647                    var n = af[id];
34648                    if(n && n.parentNode){
34649                        n.parentNode.removeChild(n);
34650                    }
34651                }
34652            }
34653         }
34654     },
34655
34656     /**
34657      * Clears the current filter. Note: with the "remove" option
34658      * set a filter cannot be cleared.
34659      */
34660     clear : function(){
34661         var t = this.tree;
34662         var af = this.filtered;
34663         for(var id in af){
34664             if(typeof id != "function"){
34665                 var n = af[id];
34666                 if(n){
34667                     n.ui.show();
34668                 }
34669             }
34670         }
34671         this.filtered = {};
34672     }
34673 };
34674 /*
34675  * Based on:
34676  * Ext JS Library 1.1.1
34677  * Copyright(c) 2006-2007, Ext JS, LLC.
34678  *
34679  * Originally Released Under LGPL - original licence link has changed is not relivant.
34680  *
34681  * Fork - LGPL
34682  * <script type="text/javascript">
34683  */
34684  
34685
34686 /**
34687  * @class Roo.tree.TreeSorter
34688  * Provides sorting of nodes in a TreePanel
34689  * 
34690  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34691  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34692  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34693  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34694  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34695  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34696  * @constructor
34697  * @param {TreePanel} tree
34698  * @param {Object} config
34699  */
34700 Roo.tree.TreeSorter = function(tree, config){
34701     Roo.apply(this, config);
34702     tree.on("beforechildrenrendered", this.doSort, this);
34703     tree.on("append", this.updateSort, this);
34704     tree.on("insert", this.updateSort, this);
34705     
34706     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34707     var p = this.property || "text";
34708     var sortType = this.sortType;
34709     var fs = this.folderSort;
34710     var cs = this.caseSensitive === true;
34711     var leafAttr = this.leafAttr || 'leaf';
34712
34713     this.sortFn = function(n1, n2){
34714         if(fs){
34715             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34716                 return 1;
34717             }
34718             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34719                 return -1;
34720             }
34721         }
34722         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34723         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34724         if(v1 < v2){
34725                         return dsc ? +1 : -1;
34726                 }else if(v1 > v2){
34727                         return dsc ? -1 : +1;
34728         }else{
34729                 return 0;
34730         }
34731     };
34732 };
34733
34734 Roo.tree.TreeSorter.prototype = {
34735     doSort : function(node){
34736         node.sort(this.sortFn);
34737     },
34738     
34739     compareNodes : function(n1, n2){
34740         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34741     },
34742     
34743     updateSort : function(tree, node){
34744         if(node.childrenRendered){
34745             this.doSort.defer(1, this, [node]);
34746         }
34747     }
34748 };/*
34749  * Based on:
34750  * Ext JS Library 1.1.1
34751  * Copyright(c) 2006-2007, Ext JS, LLC.
34752  *
34753  * Originally Released Under LGPL - original licence link has changed is not relivant.
34754  *
34755  * Fork - LGPL
34756  * <script type="text/javascript">
34757  */
34758
34759 if(Roo.dd.DropZone){
34760     
34761 Roo.tree.TreeDropZone = function(tree, config){
34762     this.allowParentInsert = false;
34763     this.allowContainerDrop = false;
34764     this.appendOnly = false;
34765     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34766     this.tree = tree;
34767     this.lastInsertClass = "x-tree-no-status";
34768     this.dragOverData = {};
34769 };
34770
34771 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34772     ddGroup : "TreeDD",
34773     scroll:  true,
34774     
34775     expandDelay : 1000,
34776     
34777     expandNode : function(node){
34778         if(node.hasChildNodes() && !node.isExpanded()){
34779             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34780         }
34781     },
34782     
34783     queueExpand : function(node){
34784         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34785     },
34786     
34787     cancelExpand : function(){
34788         if(this.expandProcId){
34789             clearTimeout(this.expandProcId);
34790             this.expandProcId = false;
34791         }
34792     },
34793     
34794     isValidDropPoint : function(n, pt, dd, e, data){
34795         if(!n || !data){ return false; }
34796         var targetNode = n.node;
34797         var dropNode = data.node;
34798         // default drop rules
34799         if(!(targetNode && targetNode.isTarget && pt)){
34800             return false;
34801         }
34802         if(pt == "append" && targetNode.allowChildren === false){
34803             return false;
34804         }
34805         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34806             return false;
34807         }
34808         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34809             return false;
34810         }
34811         // reuse the object
34812         var overEvent = this.dragOverData;
34813         overEvent.tree = this.tree;
34814         overEvent.target = targetNode;
34815         overEvent.data = data;
34816         overEvent.point = pt;
34817         overEvent.source = dd;
34818         overEvent.rawEvent = e;
34819         overEvent.dropNode = dropNode;
34820         overEvent.cancel = false;  
34821         var result = this.tree.fireEvent("nodedragover", overEvent);
34822         return overEvent.cancel === false && result !== false;
34823     },
34824     
34825     getDropPoint : function(e, n, dd)
34826     {
34827         var tn = n.node;
34828         if(tn.isRoot){
34829             return tn.allowChildren !== false ? "append" : false; // always append for root
34830         }
34831         var dragEl = n.ddel;
34832         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34833         var y = Roo.lib.Event.getPageY(e);
34834         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34835         
34836         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34837         var noAppend = tn.allowChildren === false;
34838         if(this.appendOnly || tn.parentNode.allowChildren === false){
34839             return noAppend ? false : "append";
34840         }
34841         var noBelow = false;
34842         if(!this.allowParentInsert){
34843             noBelow = tn.hasChildNodes() && tn.isExpanded();
34844         }
34845         var q = (b - t) / (noAppend ? 2 : 3);
34846         if(y >= t && y < (t + q)){
34847             return "above";
34848         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34849             return "below";
34850         }else{
34851             return "append";
34852         }
34853     },
34854     
34855     onNodeEnter : function(n, dd, e, data)
34856     {
34857         this.cancelExpand();
34858     },
34859     
34860     onNodeOver : function(n, dd, e, data)
34861     {
34862        
34863         var pt = this.getDropPoint(e, n, dd);
34864         var node = n.node;
34865         
34866         // auto node expand check
34867         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34868             this.queueExpand(node);
34869         }else if(pt != "append"){
34870             this.cancelExpand();
34871         }
34872         
34873         // set the insert point style on the target node
34874         var returnCls = this.dropNotAllowed;
34875         if(this.isValidDropPoint(n, pt, dd, e, data)){
34876            if(pt){
34877                var el = n.ddel;
34878                var cls;
34879                if(pt == "above"){
34880                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34881                    cls = "x-tree-drag-insert-above";
34882                }else if(pt == "below"){
34883                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34884                    cls = "x-tree-drag-insert-below";
34885                }else{
34886                    returnCls = "x-tree-drop-ok-append";
34887                    cls = "x-tree-drag-append";
34888                }
34889                if(this.lastInsertClass != cls){
34890                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34891                    this.lastInsertClass = cls;
34892                }
34893            }
34894        }
34895        return returnCls;
34896     },
34897     
34898     onNodeOut : function(n, dd, e, data){
34899         
34900         this.cancelExpand();
34901         this.removeDropIndicators(n);
34902     },
34903     
34904     onNodeDrop : function(n, dd, e, data){
34905         var point = this.getDropPoint(e, n, dd);
34906         var targetNode = n.node;
34907         targetNode.ui.startDrop();
34908         if(!this.isValidDropPoint(n, point, dd, e, data)){
34909             targetNode.ui.endDrop();
34910             return false;
34911         }
34912         // first try to find the drop node
34913         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34914         var dropEvent = {
34915             tree : this.tree,
34916             target: targetNode,
34917             data: data,
34918             point: point,
34919             source: dd,
34920             rawEvent: e,
34921             dropNode: dropNode,
34922             cancel: !dropNode   
34923         };
34924         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34925         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34926             targetNode.ui.endDrop();
34927             return false;
34928         }
34929         // allow target changing
34930         targetNode = dropEvent.target;
34931         if(point == "append" && !targetNode.isExpanded()){
34932             targetNode.expand(false, null, function(){
34933                 this.completeDrop(dropEvent);
34934             }.createDelegate(this));
34935         }else{
34936             this.completeDrop(dropEvent);
34937         }
34938         return true;
34939     },
34940     
34941     completeDrop : function(de){
34942         var ns = de.dropNode, p = de.point, t = de.target;
34943         if(!(ns instanceof Array)){
34944             ns = [ns];
34945         }
34946         var n;
34947         for(var i = 0, len = ns.length; i < len; i++){
34948             n = ns[i];
34949             if(p == "above"){
34950                 t.parentNode.insertBefore(n, t);
34951             }else if(p == "below"){
34952                 t.parentNode.insertBefore(n, t.nextSibling);
34953             }else{
34954                 t.appendChild(n);
34955             }
34956         }
34957         n.ui.focus();
34958         if(this.tree.hlDrop){
34959             n.ui.highlight();
34960         }
34961         t.ui.endDrop();
34962         this.tree.fireEvent("nodedrop", de);
34963     },
34964     
34965     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34966         if(this.tree.hlDrop){
34967             dropNode.ui.focus();
34968             dropNode.ui.highlight();
34969         }
34970         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34971     },
34972     
34973     getTree : function(){
34974         return this.tree;
34975     },
34976     
34977     removeDropIndicators : function(n){
34978         if(n && n.ddel){
34979             var el = n.ddel;
34980             Roo.fly(el).removeClass([
34981                     "x-tree-drag-insert-above",
34982                     "x-tree-drag-insert-below",
34983                     "x-tree-drag-append"]);
34984             this.lastInsertClass = "_noclass";
34985         }
34986     },
34987     
34988     beforeDragDrop : function(target, e, id){
34989         this.cancelExpand();
34990         return true;
34991     },
34992     
34993     afterRepair : function(data){
34994         if(data && Roo.enableFx){
34995             data.node.ui.highlight();
34996         }
34997         this.hideProxy();
34998     } 
34999     
35000 });
35001
35002 }
35003 /*
35004  * Based on:
35005  * Ext JS Library 1.1.1
35006  * Copyright(c) 2006-2007, Ext JS, LLC.
35007  *
35008  * Originally Released Under LGPL - original licence link has changed is not relivant.
35009  *
35010  * Fork - LGPL
35011  * <script type="text/javascript">
35012  */
35013  
35014
35015 if(Roo.dd.DragZone){
35016 Roo.tree.TreeDragZone = function(tree, config){
35017     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35018     this.tree = tree;
35019 };
35020
35021 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35022     ddGroup : "TreeDD",
35023    
35024     onBeforeDrag : function(data, e){
35025         var n = data.node;
35026         return n && n.draggable && !n.disabled;
35027     },
35028      
35029     
35030     onInitDrag : function(e){
35031         var data = this.dragData;
35032         this.tree.getSelectionModel().select(data.node);
35033         this.proxy.update("");
35034         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35035         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35036     },
35037     
35038     getRepairXY : function(e, data){
35039         return data.node.ui.getDDRepairXY();
35040     },
35041     
35042     onEndDrag : function(data, e){
35043         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35044         
35045         
35046     },
35047     
35048     onValidDrop : function(dd, e, id){
35049         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35050         this.hideProxy();
35051     },
35052     
35053     beforeInvalidDrop : function(e, id){
35054         // this scrolls the original position back into view
35055         var sm = this.tree.getSelectionModel();
35056         sm.clearSelections();
35057         sm.select(this.dragData.node);
35058     }
35059 });
35060 }/*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070 /**
35071  * @class Roo.tree.TreeEditor
35072  * @extends Roo.Editor
35073  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35074  * as the editor field.
35075  * @constructor
35076  * @param {Object} config (used to be the tree panel.)
35077  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35078  * 
35079  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35080  * @cfg {Roo.form.TextField|Object} field The field configuration
35081  *
35082  * 
35083  */
35084 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35085     var tree = config;
35086     var field;
35087     if (oldconfig) { // old style..
35088         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35089     } else {
35090         // new style..
35091         tree = config.tree;
35092         config.field = config.field  || {};
35093         config.field.xtype = 'TextField';
35094         field = Roo.factory(config.field, Roo.form);
35095     }
35096     config = config || {};
35097     
35098     
35099     this.addEvents({
35100         /**
35101          * @event beforenodeedit
35102          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35103          * false from the handler of this event.
35104          * @param {Editor} this
35105          * @param {Roo.tree.Node} node 
35106          */
35107         "beforenodeedit" : true
35108     });
35109     
35110     //Roo.log(config);
35111     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35112
35113     this.tree = tree;
35114
35115     tree.on('beforeclick', this.beforeNodeClick, this);
35116     tree.getTreeEl().on('mousedown', this.hide, this);
35117     this.on('complete', this.updateNode, this);
35118     this.on('beforestartedit', this.fitToTree, this);
35119     this.on('startedit', this.bindScroll, this, {delay:10});
35120     this.on('specialkey', this.onSpecialKey, this);
35121 };
35122
35123 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35124     /**
35125      * @cfg {String} alignment
35126      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35127      */
35128     alignment: "l-l",
35129     // inherit
35130     autoSize: false,
35131     /**
35132      * @cfg {Boolean} hideEl
35133      * True to hide the bound element while the editor is displayed (defaults to false)
35134      */
35135     hideEl : false,
35136     /**
35137      * @cfg {String} cls
35138      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35139      */
35140     cls: "x-small-editor x-tree-editor",
35141     /**
35142      * @cfg {Boolean} shim
35143      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35144      */
35145     shim:false,
35146     // inherit
35147     shadow:"frame",
35148     /**
35149      * @cfg {Number} maxWidth
35150      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35151      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35152      * scroll and client offsets into account prior to each edit.
35153      */
35154     maxWidth: 250,
35155
35156     editDelay : 350,
35157
35158     // private
35159     fitToTree : function(ed, el){
35160         var td = this.tree.getTreeEl().dom, nd = el.dom;
35161         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35162             td.scrollLeft = nd.offsetLeft;
35163         }
35164         var w = Math.min(
35165                 this.maxWidth,
35166                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35167         this.setSize(w, '');
35168         
35169         return this.fireEvent('beforenodeedit', this, this.editNode);
35170         
35171     },
35172
35173     // private
35174     triggerEdit : function(node){
35175         this.completeEdit();
35176         this.editNode = node;
35177         this.startEdit(node.ui.textNode, node.text);
35178     },
35179
35180     // private
35181     bindScroll : function(){
35182         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35183     },
35184
35185     // private
35186     beforeNodeClick : function(node, e){
35187         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35188         this.lastClick = new Date();
35189         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35190             e.stopEvent();
35191             this.triggerEdit(node);
35192             return false;
35193         }
35194         return true;
35195     },
35196
35197     // private
35198     updateNode : function(ed, value){
35199         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35200         this.editNode.setText(value);
35201     },
35202
35203     // private
35204     onHide : function(){
35205         Roo.tree.TreeEditor.superclass.onHide.call(this);
35206         if(this.editNode){
35207             this.editNode.ui.focus();
35208         }
35209     },
35210
35211     // private
35212     onSpecialKey : function(field, e){
35213         var k = e.getKey();
35214         if(k == e.ESC){
35215             e.stopEvent();
35216             this.cancelEdit();
35217         }else if(k == e.ENTER && !e.hasModifier()){
35218             e.stopEvent();
35219             this.completeEdit();
35220         }
35221     }
35222 });//<Script type="text/javascript">
35223 /*
35224  * Based on:
35225  * Ext JS Library 1.1.1
35226  * Copyright(c) 2006-2007, Ext JS, LLC.
35227  *
35228  * Originally Released Under LGPL - original licence link has changed is not relivant.
35229  *
35230  * Fork - LGPL
35231  * <script type="text/javascript">
35232  */
35233  
35234 /**
35235  * Not documented??? - probably should be...
35236  */
35237
35238 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35239     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35240     
35241     renderElements : function(n, a, targetNode, bulkRender){
35242         //consel.log("renderElements?");
35243         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35244
35245         var t = n.getOwnerTree();
35246         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35247         
35248         var cols = t.columns;
35249         var bw = t.borderWidth;
35250         var c = cols[0];
35251         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35252          var cb = typeof a.checked == "boolean";
35253         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35254         var colcls = 'x-t-' + tid + '-c0';
35255         var buf = [
35256             '<li class="x-tree-node">',
35257             
35258                 
35259                 '<div class="x-tree-node-el ', a.cls,'">',
35260                     // extran...
35261                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35262                 
35263                 
35264                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35265                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35266                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35267                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35268                            (a.iconCls ? ' '+a.iconCls : ''),
35269                            '" unselectable="on" />',
35270                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35271                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35272                              
35273                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35274                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35275                             '<span unselectable="on" qtip="' + tx + '">',
35276                              tx,
35277                              '</span></a>' ,
35278                     '</div>',
35279                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35280                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35281                  ];
35282         for(var i = 1, len = cols.length; i < len; i++){
35283             c = cols[i];
35284             colcls = 'x-t-' + tid + '-c' +i;
35285             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35286             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35287                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35288                       "</div>");
35289          }
35290          
35291          buf.push(
35292             '</a>',
35293             '<div class="x-clear"></div></div>',
35294             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35295             "</li>");
35296         
35297         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35298             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35299                                 n.nextSibling.ui.getEl(), buf.join(""));
35300         }else{
35301             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35302         }
35303         var el = this.wrap.firstChild;
35304         this.elRow = el;
35305         this.elNode = el.firstChild;
35306         this.ranchor = el.childNodes[1];
35307         this.ctNode = this.wrap.childNodes[1];
35308         var cs = el.firstChild.childNodes;
35309         this.indentNode = cs[0];
35310         this.ecNode = cs[1];
35311         this.iconNode = cs[2];
35312         var index = 3;
35313         if(cb){
35314             this.checkbox = cs[3];
35315             index++;
35316         }
35317         this.anchor = cs[index];
35318         
35319         this.textNode = cs[index].firstChild;
35320         
35321         //el.on("click", this.onClick, this);
35322         //el.on("dblclick", this.onDblClick, this);
35323         
35324         
35325        // console.log(this);
35326     },
35327     initEvents : function(){
35328         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35329         
35330             
35331         var a = this.ranchor;
35332
35333         var el = Roo.get(a);
35334
35335         if(Roo.isOpera){ // opera render bug ignores the CSS
35336             el.setStyle("text-decoration", "none");
35337         }
35338
35339         el.on("click", this.onClick, this);
35340         el.on("dblclick", this.onDblClick, this);
35341         el.on("contextmenu", this.onContextMenu, this);
35342         
35343     },
35344     
35345     /*onSelectedChange : function(state){
35346         if(state){
35347             this.focus();
35348             this.addClass("x-tree-selected");
35349         }else{
35350             //this.blur();
35351             this.removeClass("x-tree-selected");
35352         }
35353     },*/
35354     addClass : function(cls){
35355         if(this.elRow){
35356             Roo.fly(this.elRow).addClass(cls);
35357         }
35358         
35359     },
35360     
35361     
35362     removeClass : function(cls){
35363         if(this.elRow){
35364             Roo.fly(this.elRow).removeClass(cls);
35365         }
35366     }
35367
35368     
35369     
35370 });//<Script type="text/javascript">
35371
35372 /*
35373  * Based on:
35374  * Ext JS Library 1.1.1
35375  * Copyright(c) 2006-2007, Ext JS, LLC.
35376  *
35377  * Originally Released Under LGPL - original licence link has changed is not relivant.
35378  *
35379  * Fork - LGPL
35380  * <script type="text/javascript">
35381  */
35382  
35383
35384 /**
35385  * @class Roo.tree.ColumnTree
35386  * @extends Roo.data.TreePanel
35387  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35388  * @cfg {int} borderWidth  compined right/left border allowance
35389  * @constructor
35390  * @param {String/HTMLElement/Element} el The container element
35391  * @param {Object} config
35392  */
35393 Roo.tree.ColumnTree =  function(el, config)
35394 {
35395    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35396    this.addEvents({
35397         /**
35398         * @event resize
35399         * Fire this event on a container when it resizes
35400         * @param {int} w Width
35401         * @param {int} h Height
35402         */
35403        "resize" : true
35404     });
35405     this.on('resize', this.onResize, this);
35406 };
35407
35408 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35409     //lines:false,
35410     
35411     
35412     borderWidth: Roo.isBorderBox ? 0 : 2, 
35413     headEls : false,
35414     
35415     render : function(){
35416         // add the header.....
35417        
35418         Roo.tree.ColumnTree.superclass.render.apply(this);
35419         
35420         this.el.addClass('x-column-tree');
35421         
35422         this.headers = this.el.createChild(
35423             {cls:'x-tree-headers'},this.innerCt.dom);
35424    
35425         var cols = this.columns, c;
35426         var totalWidth = 0;
35427         this.headEls = [];
35428         var  len = cols.length;
35429         for(var i = 0; i < len; i++){
35430              c = cols[i];
35431              totalWidth += c.width;
35432             this.headEls.push(this.headers.createChild({
35433                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35434                  cn: {
35435                      cls:'x-tree-hd-text',
35436                      html: c.header
35437                  },
35438                  style:'width:'+(c.width-this.borderWidth)+'px;'
35439              }));
35440         }
35441         this.headers.createChild({cls:'x-clear'});
35442         // prevent floats from wrapping when clipped
35443         this.headers.setWidth(totalWidth);
35444         //this.innerCt.setWidth(totalWidth);
35445         this.innerCt.setStyle({ overflow: 'auto' });
35446         this.onResize(this.width, this.height);
35447              
35448         
35449     },
35450     onResize : function(w,h)
35451     {
35452         this.height = h;
35453         this.width = w;
35454         // resize cols..
35455         this.innerCt.setWidth(this.width);
35456         this.innerCt.setHeight(this.height-20);
35457         
35458         // headers...
35459         var cols = this.columns, c;
35460         var totalWidth = 0;
35461         var expEl = false;
35462         var len = cols.length;
35463         for(var i = 0; i < len; i++){
35464             c = cols[i];
35465             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35466                 // it's the expander..
35467                 expEl  = this.headEls[i];
35468                 continue;
35469             }
35470             totalWidth += c.width;
35471             
35472         }
35473         if (expEl) {
35474             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35475         }
35476         this.headers.setWidth(w-20);
35477
35478         
35479         
35480         
35481     }
35482 });
35483 /*
35484  * Based on:
35485  * Ext JS Library 1.1.1
35486  * Copyright(c) 2006-2007, Ext JS, LLC.
35487  *
35488  * Originally Released Under LGPL - original licence link has changed is not relivant.
35489  *
35490  * Fork - LGPL
35491  * <script type="text/javascript">
35492  */
35493  
35494 /**
35495  * @class Roo.menu.Menu
35496  * @extends Roo.util.Observable
35497  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35498  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35499  * @constructor
35500  * Creates a new Menu
35501  * @param {Object} config Configuration options
35502  */
35503 Roo.menu.Menu = function(config){
35504     Roo.apply(this, config);
35505     this.id = this.id || Roo.id();
35506     this.addEvents({
35507         /**
35508          * @event beforeshow
35509          * Fires before this menu is displayed
35510          * @param {Roo.menu.Menu} this
35511          */
35512         beforeshow : true,
35513         /**
35514          * @event beforehide
35515          * Fires before this menu is hidden
35516          * @param {Roo.menu.Menu} this
35517          */
35518         beforehide : true,
35519         /**
35520          * @event show
35521          * Fires after this menu is displayed
35522          * @param {Roo.menu.Menu} this
35523          */
35524         show : true,
35525         /**
35526          * @event hide
35527          * Fires after this menu is hidden
35528          * @param {Roo.menu.Menu} this
35529          */
35530         hide : true,
35531         /**
35532          * @event click
35533          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35534          * @param {Roo.menu.Menu} this
35535          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35536          * @param {Roo.EventObject} e
35537          */
35538         click : true,
35539         /**
35540          * @event mouseover
35541          * Fires when the mouse is hovering over this menu
35542          * @param {Roo.menu.Menu} this
35543          * @param {Roo.EventObject} e
35544          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35545          */
35546         mouseover : true,
35547         /**
35548          * @event mouseout
35549          * Fires when the mouse exits this menu
35550          * @param {Roo.menu.Menu} this
35551          * @param {Roo.EventObject} e
35552          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35553          */
35554         mouseout : true,
35555         /**
35556          * @event itemclick
35557          * Fires when a menu item contained in this menu is clicked
35558          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35559          * @param {Roo.EventObject} e
35560          */
35561         itemclick: true
35562     });
35563     if (this.registerMenu) {
35564         Roo.menu.MenuMgr.register(this);
35565     }
35566     
35567     var mis = this.items;
35568     this.items = new Roo.util.MixedCollection();
35569     if(mis){
35570         this.add.apply(this, mis);
35571     }
35572 };
35573
35574 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35575     /**
35576      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35577      */
35578     minWidth : 120,
35579     /**
35580      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35581      * for bottom-right shadow (defaults to "sides")
35582      */
35583     shadow : "sides",
35584     /**
35585      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35586      * this menu (defaults to "tl-tr?")
35587      */
35588     subMenuAlign : "tl-tr?",
35589     /**
35590      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35591      * relative to its element of origin (defaults to "tl-bl?")
35592      */
35593     defaultAlign : "tl-bl?",
35594     /**
35595      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35596      */
35597     allowOtherMenus : false,
35598     /**
35599      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35600      */
35601     registerMenu : true,
35602
35603     hidden:true,
35604
35605     // private
35606     render : function(){
35607         if(this.el){
35608             return;
35609         }
35610         var el = this.el = new Roo.Layer({
35611             cls: "x-menu",
35612             shadow:this.shadow,
35613             constrain: false,
35614             parentEl: this.parentEl || document.body,
35615             zindex:15000
35616         });
35617
35618         this.keyNav = new Roo.menu.MenuNav(this);
35619
35620         if(this.plain){
35621             el.addClass("x-menu-plain");
35622         }
35623         if(this.cls){
35624             el.addClass(this.cls);
35625         }
35626         // generic focus element
35627         this.focusEl = el.createChild({
35628             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35629         });
35630         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35631         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35632         
35633         ul.on("mouseover", this.onMouseOver, this);
35634         ul.on("mouseout", this.onMouseOut, this);
35635         this.items.each(function(item){
35636             if (item.hidden) {
35637                 return;
35638             }
35639             
35640             var li = document.createElement("li");
35641             li.className = "x-menu-list-item";
35642             ul.dom.appendChild(li);
35643             item.render(li, this);
35644         }, this);
35645         this.ul = ul;
35646         this.autoWidth();
35647     },
35648
35649     // private
35650     autoWidth : function(){
35651         var el = this.el, ul = this.ul;
35652         if(!el){
35653             return;
35654         }
35655         var w = this.width;
35656         if(w){
35657             el.setWidth(w);
35658         }else if(Roo.isIE){
35659             el.setWidth(this.minWidth);
35660             var t = el.dom.offsetWidth; // force recalc
35661             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35662         }
35663     },
35664
35665     // private
35666     delayAutoWidth : function(){
35667         if(this.rendered){
35668             if(!this.awTask){
35669                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35670             }
35671             this.awTask.delay(20);
35672         }
35673     },
35674
35675     // private
35676     findTargetItem : function(e){
35677         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35678         if(t && t.menuItemId){
35679             return this.items.get(t.menuItemId);
35680         }
35681     },
35682
35683     // private
35684     onClick : function(e){
35685         Roo.log("menu.onClick");
35686         var t = this.findTargetItem(e);
35687         if(!t){
35688             return;
35689         }
35690         Roo.log(e);
35691         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35692             if(t == this.activeItem && t.shouldDeactivate(e)){
35693                 this.activeItem.deactivate();
35694                 delete this.activeItem;
35695                 return;
35696             }
35697             if(t.canActivate){
35698                 this.setActiveItem(t, true);
35699             }
35700             return;
35701             
35702             
35703         }
35704         
35705         t.onClick(e);
35706         this.fireEvent("click", this, t, e);
35707     },
35708
35709     // private
35710     setActiveItem : function(item, autoExpand){
35711         if(item != this.activeItem){
35712             if(this.activeItem){
35713                 this.activeItem.deactivate();
35714             }
35715             this.activeItem = item;
35716             item.activate(autoExpand);
35717         }else if(autoExpand){
35718             item.expandMenu();
35719         }
35720     },
35721
35722     // private
35723     tryActivate : function(start, step){
35724         var items = this.items;
35725         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35726             var item = items.get(i);
35727             if(!item.disabled && item.canActivate){
35728                 this.setActiveItem(item, false);
35729                 return item;
35730             }
35731         }
35732         return false;
35733     },
35734
35735     // private
35736     onMouseOver : function(e){
35737         var t;
35738         if(t = this.findTargetItem(e)){
35739             if(t.canActivate && !t.disabled){
35740                 this.setActiveItem(t, true);
35741             }
35742         }
35743         this.fireEvent("mouseover", this, e, t);
35744     },
35745
35746     // private
35747     onMouseOut : function(e){
35748         var t;
35749         if(t = this.findTargetItem(e)){
35750             if(t == this.activeItem && t.shouldDeactivate(e)){
35751                 this.activeItem.deactivate();
35752                 delete this.activeItem;
35753             }
35754         }
35755         this.fireEvent("mouseout", this, e, t);
35756     },
35757
35758     /**
35759      * Read-only.  Returns true if the menu is currently displayed, else false.
35760      * @type Boolean
35761      */
35762     isVisible : function(){
35763         return this.el && !this.hidden;
35764     },
35765
35766     /**
35767      * Displays this menu relative to another element
35768      * @param {String/HTMLElement/Roo.Element} element The element to align to
35769      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35770      * the element (defaults to this.defaultAlign)
35771      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35772      */
35773     show : function(el, pos, parentMenu){
35774         this.parentMenu = parentMenu;
35775         if(!this.el){
35776             this.render();
35777         }
35778         this.fireEvent("beforeshow", this);
35779         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35780     },
35781
35782     /**
35783      * Displays this menu at a specific xy position
35784      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35785      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35786      */
35787     showAt : function(xy, parentMenu, /* private: */_e){
35788         this.parentMenu = parentMenu;
35789         if(!this.el){
35790             this.render();
35791         }
35792         if(_e !== false){
35793             this.fireEvent("beforeshow", this);
35794             xy = this.el.adjustForConstraints(xy);
35795         }
35796         this.el.setXY(xy);
35797         this.el.show();
35798         this.hidden = false;
35799         this.focus();
35800         this.fireEvent("show", this);
35801     },
35802
35803     focus : function(){
35804         if(!this.hidden){
35805             this.doFocus.defer(50, this);
35806         }
35807     },
35808
35809     doFocus : function(){
35810         if(!this.hidden){
35811             this.focusEl.focus();
35812         }
35813     },
35814
35815     /**
35816      * Hides this menu and optionally all parent menus
35817      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35818      */
35819     hide : function(deep){
35820         if(this.el && this.isVisible()){
35821             this.fireEvent("beforehide", this);
35822             if(this.activeItem){
35823                 this.activeItem.deactivate();
35824                 this.activeItem = null;
35825             }
35826             this.el.hide();
35827             this.hidden = true;
35828             this.fireEvent("hide", this);
35829         }
35830         if(deep === true && this.parentMenu){
35831             this.parentMenu.hide(true);
35832         }
35833     },
35834
35835     /**
35836      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35837      * Any of the following are valid:
35838      * <ul>
35839      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35840      * <li>An HTMLElement object which will be converted to a menu item</li>
35841      * <li>A menu item config object that will be created as a new menu item</li>
35842      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35843      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35844      * </ul>
35845      * Usage:
35846      * <pre><code>
35847 // Create the menu
35848 var menu = new Roo.menu.Menu();
35849
35850 // Create a menu item to add by reference
35851 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35852
35853 // Add a bunch of items at once using different methods.
35854 // Only the last item added will be returned.
35855 var item = menu.add(
35856     menuItem,                // add existing item by ref
35857     'Dynamic Item',          // new TextItem
35858     '-',                     // new separator
35859     { text: 'Config Item' }  // new item by config
35860 );
35861 </code></pre>
35862      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35863      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35864      */
35865     add : function(){
35866         var a = arguments, l = a.length, item;
35867         for(var i = 0; i < l; i++){
35868             var el = a[i];
35869             if ((typeof(el) == "object") && el.xtype && el.xns) {
35870                 el = Roo.factory(el, Roo.menu);
35871             }
35872             
35873             if(el.render){ // some kind of Item
35874                 item = this.addItem(el);
35875             }else if(typeof el == "string"){ // string
35876                 if(el == "separator" || el == "-"){
35877                     item = this.addSeparator();
35878                 }else{
35879                     item = this.addText(el);
35880                 }
35881             }else if(el.tagName || el.el){ // element
35882                 item = this.addElement(el);
35883             }else if(typeof el == "object"){ // must be menu item config?
35884                 item = this.addMenuItem(el);
35885             }
35886         }
35887         return item;
35888     },
35889
35890     /**
35891      * Returns this menu's underlying {@link Roo.Element} object
35892      * @return {Roo.Element} The element
35893      */
35894     getEl : function(){
35895         if(!this.el){
35896             this.render();
35897         }
35898         return this.el;
35899     },
35900
35901     /**
35902      * Adds a separator bar to the menu
35903      * @return {Roo.menu.Item} The menu item that was added
35904      */
35905     addSeparator : function(){
35906         return this.addItem(new Roo.menu.Separator());
35907     },
35908
35909     /**
35910      * Adds an {@link Roo.Element} object to the menu
35911      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35912      * @return {Roo.menu.Item} The menu item that was added
35913      */
35914     addElement : function(el){
35915         return this.addItem(new Roo.menu.BaseItem(el));
35916     },
35917
35918     /**
35919      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35920      * @param {Roo.menu.Item} item The menu item to add
35921      * @return {Roo.menu.Item} The menu item that was added
35922      */
35923     addItem : function(item){
35924         this.items.add(item);
35925         if(this.ul){
35926             var li = document.createElement("li");
35927             li.className = "x-menu-list-item";
35928             this.ul.dom.appendChild(li);
35929             item.render(li, this);
35930             this.delayAutoWidth();
35931         }
35932         return item;
35933     },
35934
35935     /**
35936      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35937      * @param {Object} config A MenuItem config object
35938      * @return {Roo.menu.Item} The menu item that was added
35939      */
35940     addMenuItem : function(config){
35941         if(!(config instanceof Roo.menu.Item)){
35942             if(typeof config.checked == "boolean"){ // must be check menu item config?
35943                 config = new Roo.menu.CheckItem(config);
35944             }else{
35945                 config = new Roo.menu.Item(config);
35946             }
35947         }
35948         return this.addItem(config);
35949     },
35950
35951     /**
35952      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35953      * @param {String} text The text to display in the menu item
35954      * @return {Roo.menu.Item} The menu item that was added
35955      */
35956     addText : function(text){
35957         return this.addItem(new Roo.menu.TextItem({ text : text }));
35958     },
35959
35960     /**
35961      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35962      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35963      * @param {Roo.menu.Item} item The menu item to add
35964      * @return {Roo.menu.Item} The menu item that was added
35965      */
35966     insert : function(index, item){
35967         this.items.insert(index, item);
35968         if(this.ul){
35969             var li = document.createElement("li");
35970             li.className = "x-menu-list-item";
35971             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35972             item.render(li, this);
35973             this.delayAutoWidth();
35974         }
35975         return item;
35976     },
35977
35978     /**
35979      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35980      * @param {Roo.menu.Item} item The menu item to remove
35981      */
35982     remove : function(item){
35983         this.items.removeKey(item.id);
35984         item.destroy();
35985     },
35986
35987     /**
35988      * Removes and destroys all items in the menu
35989      */
35990     removeAll : function(){
35991         var f;
35992         while(f = this.items.first()){
35993             this.remove(f);
35994         }
35995     }
35996 });
35997
35998 // MenuNav is a private utility class used internally by the Menu
35999 Roo.menu.MenuNav = function(menu){
36000     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36001     this.scope = this.menu = menu;
36002 };
36003
36004 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36005     doRelay : function(e, h){
36006         var k = e.getKey();
36007         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36008             this.menu.tryActivate(0, 1);
36009             return false;
36010         }
36011         return h.call(this.scope || this, e, this.menu);
36012     },
36013
36014     up : function(e, m){
36015         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36016             m.tryActivate(m.items.length-1, -1);
36017         }
36018     },
36019
36020     down : function(e, m){
36021         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36022             m.tryActivate(0, 1);
36023         }
36024     },
36025
36026     right : function(e, m){
36027         if(m.activeItem){
36028             m.activeItem.expandMenu(true);
36029         }
36030     },
36031
36032     left : function(e, m){
36033         m.hide();
36034         if(m.parentMenu && m.parentMenu.activeItem){
36035             m.parentMenu.activeItem.activate();
36036         }
36037     },
36038
36039     enter : function(e, m){
36040         if(m.activeItem){
36041             e.stopPropagation();
36042             m.activeItem.onClick(e);
36043             m.fireEvent("click", this, m.activeItem);
36044             return true;
36045         }
36046     }
36047 });/*
36048  * Based on:
36049  * Ext JS Library 1.1.1
36050  * Copyright(c) 2006-2007, Ext JS, LLC.
36051  *
36052  * Originally Released Under LGPL - original licence link has changed is not relivant.
36053  *
36054  * Fork - LGPL
36055  * <script type="text/javascript">
36056  */
36057  
36058 /**
36059  * @class Roo.menu.MenuMgr
36060  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36061  * @singleton
36062  */
36063 Roo.menu.MenuMgr = function(){
36064    var menus, active, groups = {}, attached = false, lastShow = new Date();
36065
36066    // private - called when first menu is created
36067    function init(){
36068        menus = {};
36069        active = new Roo.util.MixedCollection();
36070        Roo.get(document).addKeyListener(27, function(){
36071            if(active.length > 0){
36072                hideAll();
36073            }
36074        });
36075    }
36076
36077    // private
36078    function hideAll(){
36079        if(active && active.length > 0){
36080            var c = active.clone();
36081            c.each(function(m){
36082                m.hide();
36083            });
36084        }
36085    }
36086
36087    // private
36088    function onHide(m){
36089        active.remove(m);
36090        if(active.length < 1){
36091            Roo.get(document).un("mousedown", onMouseDown);
36092            attached = false;
36093        }
36094    }
36095
36096    // private
36097    function onShow(m){
36098        var last = active.last();
36099        lastShow = new Date();
36100        active.add(m);
36101        if(!attached){
36102            Roo.get(document).on("mousedown", onMouseDown);
36103            attached = true;
36104        }
36105        if(m.parentMenu){
36106           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36107           m.parentMenu.activeChild = m;
36108        }else if(last && last.isVisible()){
36109           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36110        }
36111    }
36112
36113    // private
36114    function onBeforeHide(m){
36115        if(m.activeChild){
36116            m.activeChild.hide();
36117        }
36118        if(m.autoHideTimer){
36119            clearTimeout(m.autoHideTimer);
36120            delete m.autoHideTimer;
36121        }
36122    }
36123
36124    // private
36125    function onBeforeShow(m){
36126        var pm = m.parentMenu;
36127        if(!pm && !m.allowOtherMenus){
36128            hideAll();
36129        }else if(pm && pm.activeChild && active != m){
36130            pm.activeChild.hide();
36131        }
36132    }
36133
36134    // private
36135    function onMouseDown(e){
36136        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36137            hideAll();
36138        }
36139    }
36140
36141    // private
36142    function onBeforeCheck(mi, state){
36143        if(state){
36144            var g = groups[mi.group];
36145            for(var i = 0, l = g.length; i < l; i++){
36146                if(g[i] != mi){
36147                    g[i].setChecked(false);
36148                }
36149            }
36150        }
36151    }
36152
36153    return {
36154
36155        /**
36156         * Hides all menus that are currently visible
36157         */
36158        hideAll : function(){
36159             hideAll();  
36160        },
36161
36162        // private
36163        register : function(menu){
36164            if(!menus){
36165                init();
36166            }
36167            menus[menu.id] = menu;
36168            menu.on("beforehide", onBeforeHide);
36169            menu.on("hide", onHide);
36170            menu.on("beforeshow", onBeforeShow);
36171            menu.on("show", onShow);
36172            var g = menu.group;
36173            if(g && menu.events["checkchange"]){
36174                if(!groups[g]){
36175                    groups[g] = [];
36176                }
36177                groups[g].push(menu);
36178                menu.on("checkchange", onCheck);
36179            }
36180        },
36181
36182         /**
36183          * Returns a {@link Roo.menu.Menu} object
36184          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36185          * be used to generate and return a new Menu instance.
36186          */
36187        get : function(menu){
36188            if(typeof menu == "string"){ // menu id
36189                return menus[menu];
36190            }else if(menu.events){  // menu instance
36191                return menu;
36192            }else if(typeof menu.length == 'number'){ // array of menu items?
36193                return new Roo.menu.Menu({items:menu});
36194            }else{ // otherwise, must be a config
36195                return new Roo.menu.Menu(menu);
36196            }
36197        },
36198
36199        // private
36200        unregister : function(menu){
36201            delete menus[menu.id];
36202            menu.un("beforehide", onBeforeHide);
36203            menu.un("hide", onHide);
36204            menu.un("beforeshow", onBeforeShow);
36205            menu.un("show", onShow);
36206            var g = menu.group;
36207            if(g && menu.events["checkchange"]){
36208                groups[g].remove(menu);
36209                menu.un("checkchange", onCheck);
36210            }
36211        },
36212
36213        // private
36214        registerCheckable : function(menuItem){
36215            var g = menuItem.group;
36216            if(g){
36217                if(!groups[g]){
36218                    groups[g] = [];
36219                }
36220                groups[g].push(menuItem);
36221                menuItem.on("beforecheckchange", onBeforeCheck);
36222            }
36223        },
36224
36225        // private
36226        unregisterCheckable : function(menuItem){
36227            var g = menuItem.group;
36228            if(g){
36229                groups[g].remove(menuItem);
36230                menuItem.un("beforecheckchange", onBeforeCheck);
36231            }
36232        }
36233    };
36234 }();/*
36235  * Based on:
36236  * Ext JS Library 1.1.1
36237  * Copyright(c) 2006-2007, Ext JS, LLC.
36238  *
36239  * Originally Released Under LGPL - original licence link has changed is not relivant.
36240  *
36241  * Fork - LGPL
36242  * <script type="text/javascript">
36243  */
36244  
36245
36246 /**
36247  * @class Roo.menu.BaseItem
36248  * @extends Roo.Component
36249  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36250  * management and base configuration options shared by all menu components.
36251  * @constructor
36252  * Creates a new BaseItem
36253  * @param {Object} config Configuration options
36254  */
36255 Roo.menu.BaseItem = function(config){
36256     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36257
36258     this.addEvents({
36259         /**
36260          * @event click
36261          * Fires when this item is clicked
36262          * @param {Roo.menu.BaseItem} this
36263          * @param {Roo.EventObject} e
36264          */
36265         click: true,
36266         /**
36267          * @event activate
36268          * Fires when this item is activated
36269          * @param {Roo.menu.BaseItem} this
36270          */
36271         activate : true,
36272         /**
36273          * @event deactivate
36274          * Fires when this item is deactivated
36275          * @param {Roo.menu.BaseItem} this
36276          */
36277         deactivate : true
36278     });
36279
36280     if(this.handler){
36281         this.on("click", this.handler, this.scope, true);
36282     }
36283 };
36284
36285 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36286     /**
36287      * @cfg {Function} handler
36288      * A function that will handle the click event of this menu item (defaults to undefined)
36289      */
36290     /**
36291      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36292      */
36293     canActivate : false,
36294     
36295      /**
36296      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36297      */
36298     hidden: false,
36299     
36300     /**
36301      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36302      */
36303     activeClass : "x-menu-item-active",
36304     /**
36305      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36306      */
36307     hideOnClick : true,
36308     /**
36309      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36310      */
36311     hideDelay : 100,
36312
36313     // private
36314     ctype: "Roo.menu.BaseItem",
36315
36316     // private
36317     actionMode : "container",
36318
36319     // private
36320     render : function(container, parentMenu){
36321         this.parentMenu = parentMenu;
36322         Roo.menu.BaseItem.superclass.render.call(this, container);
36323         this.container.menuItemId = this.id;
36324     },
36325
36326     // private
36327     onRender : function(container, position){
36328         this.el = Roo.get(this.el);
36329         container.dom.appendChild(this.el.dom);
36330     },
36331
36332     // private
36333     onClick : function(e){
36334         if(!this.disabled && this.fireEvent("click", this, e) !== false
36335                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36336             this.handleClick(e);
36337         }else{
36338             e.stopEvent();
36339         }
36340     },
36341
36342     // private
36343     activate : function(){
36344         if(this.disabled){
36345             return false;
36346         }
36347         var li = this.container;
36348         li.addClass(this.activeClass);
36349         this.region = li.getRegion().adjust(2, 2, -2, -2);
36350         this.fireEvent("activate", this);
36351         return true;
36352     },
36353
36354     // private
36355     deactivate : function(){
36356         this.container.removeClass(this.activeClass);
36357         this.fireEvent("deactivate", this);
36358     },
36359
36360     // private
36361     shouldDeactivate : function(e){
36362         return !this.region || !this.region.contains(e.getPoint());
36363     },
36364
36365     // private
36366     handleClick : function(e){
36367         if(this.hideOnClick){
36368             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36369         }
36370     },
36371
36372     // private
36373     expandMenu : function(autoActivate){
36374         // do nothing
36375     },
36376
36377     // private
36378     hideMenu : function(){
36379         // do nothing
36380     }
36381 });/*
36382  * Based on:
36383  * Ext JS Library 1.1.1
36384  * Copyright(c) 2006-2007, Ext JS, LLC.
36385  *
36386  * Originally Released Under LGPL - original licence link has changed is not relivant.
36387  *
36388  * Fork - LGPL
36389  * <script type="text/javascript">
36390  */
36391  
36392 /**
36393  * @class Roo.menu.Adapter
36394  * @extends Roo.menu.BaseItem
36395  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
36396  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36397  * @constructor
36398  * Creates a new Adapter
36399  * @param {Object} config Configuration options
36400  */
36401 Roo.menu.Adapter = function(component, config){
36402     Roo.menu.Adapter.superclass.constructor.call(this, config);
36403     this.component = component;
36404 };
36405 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36406     // private
36407     canActivate : true,
36408
36409     // private
36410     onRender : function(container, position){
36411         this.component.render(container);
36412         this.el = this.component.getEl();
36413     },
36414
36415     // private
36416     activate : function(){
36417         if(this.disabled){
36418             return false;
36419         }
36420         this.component.focus();
36421         this.fireEvent("activate", this);
36422         return true;
36423     },
36424
36425     // private
36426     deactivate : function(){
36427         this.fireEvent("deactivate", this);
36428     },
36429
36430     // private
36431     disable : function(){
36432         this.component.disable();
36433         Roo.menu.Adapter.superclass.disable.call(this);
36434     },
36435
36436     // private
36437     enable : function(){
36438         this.component.enable();
36439         Roo.menu.Adapter.superclass.enable.call(this);
36440     }
36441 });/*
36442  * Based on:
36443  * Ext JS Library 1.1.1
36444  * Copyright(c) 2006-2007, Ext JS, LLC.
36445  *
36446  * Originally Released Under LGPL - original licence link has changed is not relivant.
36447  *
36448  * Fork - LGPL
36449  * <script type="text/javascript">
36450  */
36451
36452 /**
36453  * @class Roo.menu.TextItem
36454  * @extends Roo.menu.BaseItem
36455  * Adds a static text string to a menu, usually used as either a heading or group separator.
36456  * Note: old style constructor with text is still supported.
36457  * 
36458  * @constructor
36459  * Creates a new TextItem
36460  * @param {Object} cfg Configuration
36461  */
36462 Roo.menu.TextItem = function(cfg){
36463     if (typeof(cfg) == 'string') {
36464         this.text = cfg;
36465     } else {
36466         Roo.apply(this,cfg);
36467     }
36468     
36469     Roo.menu.TextItem.superclass.constructor.call(this);
36470 };
36471
36472 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36473     /**
36474      * @cfg {Boolean} text Text to show on item.
36475      */
36476     text : '',
36477     
36478     /**
36479      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36480      */
36481     hideOnClick : false,
36482     /**
36483      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36484      */
36485     itemCls : "x-menu-text",
36486
36487     // private
36488     onRender : function(){
36489         var s = document.createElement("span");
36490         s.className = this.itemCls;
36491         s.innerHTML = this.text;
36492         this.el = s;
36493         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36494     }
36495 });/*
36496  * Based on:
36497  * Ext JS Library 1.1.1
36498  * Copyright(c) 2006-2007, Ext JS, LLC.
36499  *
36500  * Originally Released Under LGPL - original licence link has changed is not relivant.
36501  *
36502  * Fork - LGPL
36503  * <script type="text/javascript">
36504  */
36505
36506 /**
36507  * @class Roo.menu.Separator
36508  * @extends Roo.menu.BaseItem
36509  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36510  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36511  * @constructor
36512  * @param {Object} config Configuration options
36513  */
36514 Roo.menu.Separator = function(config){
36515     Roo.menu.Separator.superclass.constructor.call(this, config);
36516 };
36517
36518 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36519     /**
36520      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36521      */
36522     itemCls : "x-menu-sep",
36523     /**
36524      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36525      */
36526     hideOnClick : false,
36527
36528     // private
36529     onRender : function(li){
36530         var s = document.createElement("span");
36531         s.className = this.itemCls;
36532         s.innerHTML = "&#160;";
36533         this.el = s;
36534         li.addClass("x-menu-sep-li");
36535         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36536     }
36537 });/*
36538  * Based on:
36539  * Ext JS Library 1.1.1
36540  * Copyright(c) 2006-2007, Ext JS, LLC.
36541  *
36542  * Originally Released Under LGPL - original licence link has changed is not relivant.
36543  *
36544  * Fork - LGPL
36545  * <script type="text/javascript">
36546  */
36547 /**
36548  * @class Roo.menu.Item
36549  * @extends Roo.menu.BaseItem
36550  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36551  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36552  * activation and click handling.
36553  * @constructor
36554  * Creates a new Item
36555  * @param {Object} config Configuration options
36556  */
36557 Roo.menu.Item = function(config){
36558     Roo.menu.Item.superclass.constructor.call(this, config);
36559     if(this.menu){
36560         this.menu = Roo.menu.MenuMgr.get(this.menu);
36561     }
36562 };
36563 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36564     
36565     /**
36566      * @cfg {String} text
36567      * The text to show on the menu item.
36568      */
36569     text: '',
36570      /**
36571      * @cfg {String} HTML to render in menu
36572      * The text to show on the menu item (HTML version).
36573      */
36574     html: '',
36575     /**
36576      * @cfg {String} icon
36577      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36578      */
36579     icon: undefined,
36580     /**
36581      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36582      */
36583     itemCls : "x-menu-item",
36584     /**
36585      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36586      */
36587     canActivate : true,
36588     /**
36589      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36590      */
36591     showDelay: 200,
36592     // doc'd in BaseItem
36593     hideDelay: 200,
36594
36595     // private
36596     ctype: "Roo.menu.Item",
36597     
36598     // private
36599     onRender : function(container, position){
36600         var el = document.createElement("a");
36601         el.hideFocus = true;
36602         el.unselectable = "on";
36603         el.href = this.href || "#";
36604         if(this.hrefTarget){
36605             el.target = this.hrefTarget;
36606         }
36607         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36608         
36609         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36610         
36611         el.innerHTML = String.format(
36612                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36613                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36614         this.el = el;
36615         Roo.menu.Item.superclass.onRender.call(this, container, position);
36616     },
36617
36618     /**
36619      * Sets the text to display in this menu item
36620      * @param {String} text The text to display
36621      * @param {Boolean} isHTML true to indicate text is pure html.
36622      */
36623     setText : function(text, isHTML){
36624         if (isHTML) {
36625             this.html = text;
36626         } else {
36627             this.text = text;
36628             this.html = '';
36629         }
36630         if(this.rendered){
36631             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36632      
36633             this.el.update(String.format(
36634                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36635                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36636             this.parentMenu.autoWidth();
36637         }
36638     },
36639
36640     // private
36641     handleClick : function(e){
36642         if(!this.href){ // if no link defined, stop the event automatically
36643             e.stopEvent();
36644         }
36645         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36646     },
36647
36648     // private
36649     activate : function(autoExpand){
36650         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36651             this.focus();
36652             if(autoExpand){
36653                 this.expandMenu();
36654             }
36655         }
36656         return true;
36657     },
36658
36659     // private
36660     shouldDeactivate : function(e){
36661         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36662             if(this.menu && this.menu.isVisible()){
36663                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36664             }
36665             return true;
36666         }
36667         return false;
36668     },
36669
36670     // private
36671     deactivate : function(){
36672         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36673         this.hideMenu();
36674     },
36675
36676     // private
36677     expandMenu : function(autoActivate){
36678         if(!this.disabled && this.menu){
36679             clearTimeout(this.hideTimer);
36680             delete this.hideTimer;
36681             if(!this.menu.isVisible() && !this.showTimer){
36682                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36683             }else if (this.menu.isVisible() && autoActivate){
36684                 this.menu.tryActivate(0, 1);
36685             }
36686         }
36687     },
36688
36689     // private
36690     deferExpand : function(autoActivate){
36691         delete this.showTimer;
36692         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36693         if(autoActivate){
36694             this.menu.tryActivate(0, 1);
36695         }
36696     },
36697
36698     // private
36699     hideMenu : function(){
36700         clearTimeout(this.showTimer);
36701         delete this.showTimer;
36702         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36703             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36704         }
36705     },
36706
36707     // private
36708     deferHide : function(){
36709         delete this.hideTimer;
36710         this.menu.hide();
36711     }
36712 });/*
36713  * Based on:
36714  * Ext JS Library 1.1.1
36715  * Copyright(c) 2006-2007, Ext JS, LLC.
36716  *
36717  * Originally Released Under LGPL - original licence link has changed is not relivant.
36718  *
36719  * Fork - LGPL
36720  * <script type="text/javascript">
36721  */
36722  
36723 /**
36724  * @class Roo.menu.CheckItem
36725  * @extends Roo.menu.Item
36726  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36727  * @constructor
36728  * Creates a new CheckItem
36729  * @param {Object} config Configuration options
36730  */
36731 Roo.menu.CheckItem = function(config){
36732     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36733     this.addEvents({
36734         /**
36735          * @event beforecheckchange
36736          * Fires before the checked value is set, providing an opportunity to cancel if needed
36737          * @param {Roo.menu.CheckItem} this
36738          * @param {Boolean} checked The new checked value that will be set
36739          */
36740         "beforecheckchange" : true,
36741         /**
36742          * @event checkchange
36743          * Fires after the checked value has been set
36744          * @param {Roo.menu.CheckItem} this
36745          * @param {Boolean} checked The checked value that was set
36746          */
36747         "checkchange" : true
36748     });
36749     if(this.checkHandler){
36750         this.on('checkchange', this.checkHandler, this.scope);
36751     }
36752 };
36753 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36754     /**
36755      * @cfg {String} group
36756      * All check items with the same group name will automatically be grouped into a single-select
36757      * radio button group (defaults to '')
36758      */
36759     /**
36760      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36761      */
36762     itemCls : "x-menu-item x-menu-check-item",
36763     /**
36764      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36765      */
36766     groupClass : "x-menu-group-item",
36767
36768     /**
36769      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36770      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36771      * initialized with checked = true will be rendered as checked.
36772      */
36773     checked: false,
36774
36775     // private
36776     ctype: "Roo.menu.CheckItem",
36777
36778     // private
36779     onRender : function(c){
36780         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36781         if(this.group){
36782             this.el.addClass(this.groupClass);
36783         }
36784         Roo.menu.MenuMgr.registerCheckable(this);
36785         if(this.checked){
36786             this.checked = false;
36787             this.setChecked(true, true);
36788         }
36789     },
36790
36791     // private
36792     destroy : function(){
36793         if(this.rendered){
36794             Roo.menu.MenuMgr.unregisterCheckable(this);
36795         }
36796         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36797     },
36798
36799     /**
36800      * Set the checked state of this item
36801      * @param {Boolean} checked The new checked value
36802      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36803      */
36804     setChecked : function(state, suppressEvent){
36805         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36806             if(this.container){
36807                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36808             }
36809             this.checked = state;
36810             if(suppressEvent !== true){
36811                 this.fireEvent("checkchange", this, state);
36812             }
36813         }
36814     },
36815
36816     // private
36817     handleClick : function(e){
36818        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36819            this.setChecked(!this.checked);
36820        }
36821        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36822     }
36823 });/*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833  
36834 /**
36835  * @class Roo.menu.DateItem
36836  * @extends Roo.menu.Adapter
36837  * A menu item that wraps the {@link Roo.DatPicker} component.
36838  * @constructor
36839  * Creates a new DateItem
36840  * @param {Object} config Configuration options
36841  */
36842 Roo.menu.DateItem = function(config){
36843     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36844     /** The Roo.DatePicker object @type Roo.DatePicker */
36845     this.picker = this.component;
36846     this.addEvents({select: true});
36847     
36848     this.picker.on("render", function(picker){
36849         picker.getEl().swallowEvent("click");
36850         picker.container.addClass("x-menu-date-item");
36851     });
36852
36853     this.picker.on("select", this.onSelect, this);
36854 };
36855
36856 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36857     // private
36858     onSelect : function(picker, date){
36859         this.fireEvent("select", this, date, picker);
36860         Roo.menu.DateItem.superclass.handleClick.call(this);
36861     }
36862 });/*
36863  * Based on:
36864  * Ext JS Library 1.1.1
36865  * Copyright(c) 2006-2007, Ext JS, LLC.
36866  *
36867  * Originally Released Under LGPL - original licence link has changed is not relivant.
36868  *
36869  * Fork - LGPL
36870  * <script type="text/javascript">
36871  */
36872  
36873 /**
36874  * @class Roo.menu.ColorItem
36875  * @extends Roo.menu.Adapter
36876  * A menu item that wraps the {@link Roo.ColorPalette} component.
36877  * @constructor
36878  * Creates a new ColorItem
36879  * @param {Object} config Configuration options
36880  */
36881 Roo.menu.ColorItem = function(config){
36882     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36883     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36884     this.palette = this.component;
36885     this.relayEvents(this.palette, ["select"]);
36886     if(this.selectHandler){
36887         this.on('select', this.selectHandler, this.scope);
36888     }
36889 };
36890 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36891  * Based on:
36892  * Ext JS Library 1.1.1
36893  * Copyright(c) 2006-2007, Ext JS, LLC.
36894  *
36895  * Originally Released Under LGPL - original licence link has changed is not relivant.
36896  *
36897  * Fork - LGPL
36898  * <script type="text/javascript">
36899  */
36900  
36901
36902 /**
36903  * @class Roo.menu.DateMenu
36904  * @extends Roo.menu.Menu
36905  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36906  * @constructor
36907  * Creates a new DateMenu
36908  * @param {Object} config Configuration options
36909  */
36910 Roo.menu.DateMenu = function(config){
36911     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36912     this.plain = true;
36913     var di = new Roo.menu.DateItem(config);
36914     this.add(di);
36915     /**
36916      * The {@link Roo.DatePicker} instance for this DateMenu
36917      * @type DatePicker
36918      */
36919     this.picker = di.picker;
36920     /**
36921      * @event select
36922      * @param {DatePicker} picker
36923      * @param {Date} date
36924      */
36925     this.relayEvents(di, ["select"]);
36926     this.on('beforeshow', function(){
36927         if(this.picker){
36928             this.picker.hideMonthPicker(false);
36929         }
36930     }, this);
36931 };
36932 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36933     cls:'x-date-menu'
36934 });/*
36935  * Based on:
36936  * Ext JS Library 1.1.1
36937  * Copyright(c) 2006-2007, Ext JS, LLC.
36938  *
36939  * Originally Released Under LGPL - original licence link has changed is not relivant.
36940  *
36941  * Fork - LGPL
36942  * <script type="text/javascript">
36943  */
36944  
36945
36946 /**
36947  * @class Roo.menu.ColorMenu
36948  * @extends Roo.menu.Menu
36949  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36950  * @constructor
36951  * Creates a new ColorMenu
36952  * @param {Object} config Configuration options
36953  */
36954 Roo.menu.ColorMenu = function(config){
36955     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36956     this.plain = true;
36957     var ci = new Roo.menu.ColorItem(config);
36958     this.add(ci);
36959     /**
36960      * The {@link Roo.ColorPalette} instance for this ColorMenu
36961      * @type ColorPalette
36962      */
36963     this.palette = ci.palette;
36964     /**
36965      * @event select
36966      * @param {ColorPalette} palette
36967      * @param {String} color
36968      */
36969     this.relayEvents(ci, ["select"]);
36970 };
36971 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36972  * Based on:
36973  * Ext JS Library 1.1.1
36974  * Copyright(c) 2006-2007, Ext JS, LLC.
36975  *
36976  * Originally Released Under LGPL - original licence link has changed is not relivant.
36977  *
36978  * Fork - LGPL
36979  * <script type="text/javascript">
36980  */
36981  
36982 /**
36983  * @class Roo.form.Field
36984  * @extends Roo.BoxComponent
36985  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36986  * @constructor
36987  * Creates a new Field
36988  * @param {Object} config Configuration options
36989  */
36990 Roo.form.Field = function(config){
36991     Roo.form.Field.superclass.constructor.call(this, config);
36992 };
36993
36994 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36995     /**
36996      * @cfg {String} fieldLabel Label to use when rendering a form.
36997      */
36998        /**
36999      * @cfg {String} qtip Mouse over tip
37000      */
37001      
37002     /**
37003      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37004      */
37005     invalidClass : "x-form-invalid",
37006     /**
37007      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
37008      */
37009     invalidText : "The value in this field is invalid",
37010     /**
37011      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37012      */
37013     focusClass : "x-form-focus",
37014     /**
37015      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37016       automatic validation (defaults to "keyup").
37017      */
37018     validationEvent : "keyup",
37019     /**
37020      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37021      */
37022     validateOnBlur : true,
37023     /**
37024      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37025      */
37026     validationDelay : 250,
37027     /**
37028      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37029      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37030      */
37031     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37032     /**
37033      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37034      */
37035     fieldClass : "x-form-field",
37036     /**
37037      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37038      *<pre>
37039 Value         Description
37040 -----------   ----------------------------------------------------------------------
37041 qtip          Display a quick tip when the user hovers over the field
37042 title         Display a default browser title attribute popup
37043 under         Add a block div beneath the field containing the error text
37044 side          Add an error icon to the right of the field with a popup on hover
37045 [element id]  Add the error text directly to the innerHTML of the specified element
37046 </pre>
37047      */
37048     msgTarget : 'qtip',
37049     /**
37050      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37051      */
37052     msgFx : 'normal',
37053
37054     /**
37055      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
37056      */
37057     readOnly : false,
37058
37059     /**
37060      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37061      */
37062     disabled : false,
37063
37064     /**
37065      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37066      */
37067     inputType : undefined,
37068     
37069     /**
37070      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
37071          */
37072         tabIndex : undefined,
37073         
37074     // private
37075     isFormField : true,
37076
37077     // private
37078     hasFocus : false,
37079     /**
37080      * @property {Roo.Element} fieldEl
37081      * Element Containing the rendered Field (with label etc.)
37082      */
37083     /**
37084      * @cfg {Mixed} value A value to initialize this field with.
37085      */
37086     value : undefined,
37087
37088     /**
37089      * @cfg {String} name The field's HTML name attribute.
37090      */
37091     /**
37092      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37093      */
37094
37095         // private ??
37096         initComponent : function(){
37097         Roo.form.Field.superclass.initComponent.call(this);
37098         this.addEvents({
37099             /**
37100              * @event focus
37101              * Fires when this field receives input focus.
37102              * @param {Roo.form.Field} this
37103              */
37104             focus : true,
37105             /**
37106              * @event blur
37107              * Fires when this field loses input focus.
37108              * @param {Roo.form.Field} this
37109              */
37110             blur : true,
37111             /**
37112              * @event specialkey
37113              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37114              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37115              * @param {Roo.form.Field} this
37116              * @param {Roo.EventObject} e The event object
37117              */
37118             specialkey : true,
37119             /**
37120              * @event change
37121              * Fires just before the field blurs if the field value has changed.
37122              * @param {Roo.form.Field} this
37123              * @param {Mixed} newValue The new value
37124              * @param {Mixed} oldValue The original value
37125              */
37126             change : true,
37127             /**
37128              * @event invalid
37129              * Fires after the field has been marked as invalid.
37130              * @param {Roo.form.Field} this
37131              * @param {String} msg The validation message
37132              */
37133             invalid : true,
37134             /**
37135              * @event valid
37136              * Fires after the field has been validated with no errors.
37137              * @param {Roo.form.Field} this
37138              */
37139             valid : true,
37140              /**
37141              * @event keyup
37142              * Fires after the key up
37143              * @param {Roo.form.Field} this
37144              * @param {Roo.EventObject}  e The event Object
37145              */
37146             keyup : true
37147         });
37148     },
37149
37150     /**
37151      * Returns the name attribute of the field if available
37152      * @return {String} name The field name
37153      */
37154     getName: function(){
37155          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37156     },
37157
37158     // private
37159     onRender : function(ct, position){
37160         Roo.form.Field.superclass.onRender.call(this, ct, position);
37161         if(!this.el){
37162             var cfg = this.getAutoCreate();
37163             if(!cfg.name){
37164                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37165             }
37166             if (!cfg.name.length) {
37167                 delete cfg.name;
37168             }
37169             if(this.inputType){
37170                 cfg.type = this.inputType;
37171             }
37172             this.el = ct.createChild(cfg, position);
37173         }
37174         var type = this.el.dom.type;
37175         if(type){
37176             if(type == 'password'){
37177                 type = 'text';
37178             }
37179             this.el.addClass('x-form-'+type);
37180         }
37181         if(this.readOnly){
37182             this.el.dom.readOnly = true;
37183         }
37184         if(this.tabIndex !== undefined){
37185             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37186         }
37187
37188         this.el.addClass([this.fieldClass, this.cls]);
37189         this.initValue();
37190     },
37191
37192     /**
37193      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37194      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37195      * @return {Roo.form.Field} this
37196      */
37197     applyTo : function(target){
37198         this.allowDomMove = false;
37199         this.el = Roo.get(target);
37200         this.render(this.el.dom.parentNode);
37201         return this;
37202     },
37203
37204     // private
37205     initValue : function(){
37206         if(this.value !== undefined){
37207             this.setValue(this.value);
37208         }else if(this.el.dom.value.length > 0){
37209             this.setValue(this.el.dom.value);
37210         }
37211     },
37212
37213     /**
37214      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37215      */
37216     isDirty : function() {
37217         if(this.disabled) {
37218             return false;
37219         }
37220         return String(this.getValue()) !== String(this.originalValue);
37221     },
37222
37223     // private
37224     afterRender : function(){
37225         Roo.form.Field.superclass.afterRender.call(this);
37226         this.initEvents();
37227     },
37228
37229     // private
37230     fireKey : function(e){
37231         //Roo.log('field ' + e.getKey());
37232         if(e.isNavKeyPress()){
37233             this.fireEvent("specialkey", this, e);
37234         }
37235     },
37236
37237     /**
37238      * Resets the current field value to the originally loaded value and clears any validation messages
37239      */
37240     reset : function(){
37241         this.setValue(this.resetValue);
37242         this.clearInvalid();
37243     },
37244
37245     // private
37246     initEvents : function(){
37247         // safari killled keypress - so keydown is now used..
37248         this.el.on("keydown" , this.fireKey,  this);
37249         this.el.on("focus", this.onFocus,  this);
37250         this.el.on("blur", this.onBlur,  this);
37251         this.el.relayEvent('keyup', this);
37252
37253         // reference to original value for reset
37254         this.originalValue = this.getValue();
37255         this.resetValue =  this.getValue();
37256     },
37257
37258     // private
37259     onFocus : function(){
37260         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37261             this.el.addClass(this.focusClass);
37262         }
37263         if(!this.hasFocus){
37264             this.hasFocus = true;
37265             this.startValue = this.getValue();
37266             this.fireEvent("focus", this);
37267         }
37268     },
37269
37270     beforeBlur : Roo.emptyFn,
37271
37272     // private
37273     onBlur : function(){
37274         this.beforeBlur();
37275         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37276             this.el.removeClass(this.focusClass);
37277         }
37278         this.hasFocus = false;
37279         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37280             this.validate();
37281         }
37282         var v = this.getValue();
37283         if(String(v) !== String(this.startValue)){
37284             this.fireEvent('change', this, v, this.startValue);
37285         }
37286         this.fireEvent("blur", this);
37287     },
37288
37289     /**
37290      * Returns whether or not the field value is currently valid
37291      * @param {Boolean} preventMark True to disable marking the field invalid
37292      * @return {Boolean} True if the value is valid, else false
37293      */
37294     isValid : function(preventMark){
37295         if(this.disabled){
37296             return true;
37297         }
37298         var restore = this.preventMark;
37299         this.preventMark = preventMark === true;
37300         var v = this.validateValue(this.processValue(this.getRawValue()));
37301         this.preventMark = restore;
37302         return v;
37303     },
37304
37305     /**
37306      * Validates the field value
37307      * @return {Boolean} True if the value is valid, else false
37308      */
37309     validate : function(){
37310         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37311             this.clearInvalid();
37312             return true;
37313         }
37314         return false;
37315     },
37316
37317     processValue : function(value){
37318         return value;
37319     },
37320
37321     // private
37322     // Subclasses should provide the validation implementation by overriding this
37323     validateValue : function(value){
37324         return true;
37325     },
37326
37327     /**
37328      * Mark this field as invalid
37329      * @param {String} msg The validation message
37330      */
37331     markInvalid : function(msg){
37332         if(!this.rendered || this.preventMark){ // not rendered
37333             return;
37334         }
37335         
37336         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37337         
37338         obj.el.addClass(this.invalidClass);
37339         msg = msg || this.invalidText;
37340         switch(this.msgTarget){
37341             case 'qtip':
37342                 obj.el.dom.qtip = msg;
37343                 obj.el.dom.qclass = 'x-form-invalid-tip';
37344                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37345                     Roo.QuickTips.enable();
37346                 }
37347                 break;
37348             case 'title':
37349                 this.el.dom.title = msg;
37350                 break;
37351             case 'under':
37352                 if(!this.errorEl){
37353                     var elp = this.el.findParent('.x-form-element', 5, true);
37354                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37355                     this.errorEl.setWidth(elp.getWidth(true)-20);
37356                 }
37357                 this.errorEl.update(msg);
37358                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37359                 break;
37360             case 'side':
37361                 if(!this.errorIcon){
37362                     var elp = this.el.findParent('.x-form-element', 5, true);
37363                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37364                 }
37365                 this.alignErrorIcon();
37366                 this.errorIcon.dom.qtip = msg;
37367                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37368                 this.errorIcon.show();
37369                 this.on('resize', this.alignErrorIcon, this);
37370                 break;
37371             default:
37372                 var t = Roo.getDom(this.msgTarget);
37373                 t.innerHTML = msg;
37374                 t.style.display = this.msgDisplay;
37375                 break;
37376         }
37377         this.fireEvent('invalid', this, msg);
37378     },
37379
37380     // private
37381     alignErrorIcon : function(){
37382         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37383     },
37384
37385     /**
37386      * Clear any invalid styles/messages for this field
37387      */
37388     clearInvalid : function(){
37389         if(!this.rendered || this.preventMark){ // not rendered
37390             return;
37391         }
37392         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37393         
37394         obj.el.removeClass(this.invalidClass);
37395         switch(this.msgTarget){
37396             case 'qtip':
37397                 obj.el.dom.qtip = '';
37398                 break;
37399             case 'title':
37400                 this.el.dom.title = '';
37401                 break;
37402             case 'under':
37403                 if(this.errorEl){
37404                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37405                 }
37406                 break;
37407             case 'side':
37408                 if(this.errorIcon){
37409                     this.errorIcon.dom.qtip = '';
37410                     this.errorIcon.hide();
37411                     this.un('resize', this.alignErrorIcon, this);
37412                 }
37413                 break;
37414             default:
37415                 var t = Roo.getDom(this.msgTarget);
37416                 t.innerHTML = '';
37417                 t.style.display = 'none';
37418                 break;
37419         }
37420         this.fireEvent('valid', this);
37421     },
37422
37423     /**
37424      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37425      * @return {Mixed} value The field value
37426      */
37427     getRawValue : function(){
37428         var v = this.el.getValue();
37429         
37430         return v;
37431     },
37432
37433     /**
37434      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37435      * @return {Mixed} value The field value
37436      */
37437     getValue : function(){
37438         var v = this.el.getValue();
37439          
37440         return v;
37441     },
37442
37443     /**
37444      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37445      * @param {Mixed} value The value to set
37446      */
37447     setRawValue : function(v){
37448         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37449     },
37450
37451     /**
37452      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37453      * @param {Mixed} value The value to set
37454      */
37455     setValue : function(v){
37456         this.value = v;
37457         if(this.rendered){
37458             this.el.dom.value = (v === null || v === undefined ? '' : v);
37459              this.validate();
37460         }
37461     },
37462
37463     adjustSize : function(w, h){
37464         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37465         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37466         return s;
37467     },
37468
37469     adjustWidth : function(tag, w){
37470         tag = tag.toLowerCase();
37471         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37472             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37473                 if(tag == 'input'){
37474                     return w + 2;
37475                 }
37476                 if(tag == 'textarea'){
37477                     return w-2;
37478                 }
37479             }else if(Roo.isOpera){
37480                 if(tag == 'input'){
37481                     return w + 2;
37482                 }
37483                 if(tag == 'textarea'){
37484                     return w-2;
37485                 }
37486             }
37487         }
37488         return w;
37489     }
37490 });
37491
37492
37493 // anything other than normal should be considered experimental
37494 Roo.form.Field.msgFx = {
37495     normal : {
37496         show: function(msgEl, f){
37497             msgEl.setDisplayed('block');
37498         },
37499
37500         hide : function(msgEl, f){
37501             msgEl.setDisplayed(false).update('');
37502         }
37503     },
37504
37505     slide : {
37506         show: function(msgEl, f){
37507             msgEl.slideIn('t', {stopFx:true});
37508         },
37509
37510         hide : function(msgEl, f){
37511             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37512         }
37513     },
37514
37515     slideRight : {
37516         show: function(msgEl, f){
37517             msgEl.fixDisplay();
37518             msgEl.alignTo(f.el, 'tl-tr');
37519             msgEl.slideIn('l', {stopFx:true});
37520         },
37521
37522         hide : function(msgEl, f){
37523             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37524         }
37525     }
37526 };/*
37527  * Based on:
37528  * Ext JS Library 1.1.1
37529  * Copyright(c) 2006-2007, Ext JS, LLC.
37530  *
37531  * Originally Released Under LGPL - original licence link has changed is not relivant.
37532  *
37533  * Fork - LGPL
37534  * <script type="text/javascript">
37535  */
37536  
37537
37538 /**
37539  * @class Roo.form.TextField
37540  * @extends Roo.form.Field
37541  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37542  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37543  * @constructor
37544  * Creates a new TextField
37545  * @param {Object} config Configuration options
37546  */
37547 Roo.form.TextField = function(config){
37548     Roo.form.TextField.superclass.constructor.call(this, config);
37549     this.addEvents({
37550         /**
37551          * @event autosize
37552          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37553          * according to the default logic, but this event provides a hook for the developer to apply additional
37554          * logic at runtime to resize the field if needed.
37555              * @param {Roo.form.Field} this This text field
37556              * @param {Number} width The new field width
37557              */
37558         autosize : true
37559     });
37560 };
37561
37562 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37563     /**
37564      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37565      */
37566     grow : false,
37567     /**
37568      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37569      */
37570     growMin : 30,
37571     /**
37572      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37573      */
37574     growMax : 800,
37575     /**
37576      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37577      */
37578     vtype : null,
37579     /**
37580      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37581      */
37582     maskRe : null,
37583     /**
37584      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37585      */
37586     disableKeyFilter : false,
37587     /**
37588      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37589      */
37590     allowBlank : true,
37591     /**
37592      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37593      */
37594     minLength : 0,
37595     /**
37596      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37597      */
37598     maxLength : Number.MAX_VALUE,
37599     /**
37600      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37601      */
37602     minLengthText : "The minimum length for this field is {0}",
37603     /**
37604      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37605      */
37606     maxLengthText : "The maximum length for this field is {0}",
37607     /**
37608      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37609      */
37610     selectOnFocus : false,
37611     /**
37612      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37613      */
37614     blankText : "This field is required",
37615     /**
37616      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37617      * If available, this function will be called only after the basic validators all return true, and will be passed the
37618      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37619      */
37620     validator : null,
37621     /**
37622      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37623      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37624      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37625      */
37626     regex : null,
37627     /**
37628      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37629      */
37630     regexText : "",
37631     /**
37632      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37633      */
37634     emptyText : null,
37635    
37636
37637     // private
37638     initEvents : function()
37639     {
37640         if (this.emptyText) {
37641             this.el.attr('placeholder', this.emptyText);
37642         }
37643         
37644         Roo.form.TextField.superclass.initEvents.call(this);
37645         if(this.validationEvent == 'keyup'){
37646             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37647             this.el.on('keyup', this.filterValidation, this);
37648         }
37649         else if(this.validationEvent !== false){
37650             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37651         }
37652         
37653         if(this.selectOnFocus){
37654             this.on("focus", this.preFocus, this);
37655             
37656         }
37657         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37658             this.el.on("keypress", this.filterKeys, this);
37659         }
37660         if(this.grow){
37661             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37662             this.el.on("click", this.autoSize,  this);
37663         }
37664         if(this.el.is('input[type=password]') && Roo.isSafari){
37665             this.el.on('keydown', this.SafariOnKeyDown, this);
37666         }
37667     },
37668
37669     processValue : function(value){
37670         if(this.stripCharsRe){
37671             var newValue = value.replace(this.stripCharsRe, '');
37672             if(newValue !== value){
37673                 this.setRawValue(newValue);
37674                 return newValue;
37675             }
37676         }
37677         return value;
37678     },
37679
37680     filterValidation : function(e){
37681         if(!e.isNavKeyPress()){
37682             this.validationTask.delay(this.validationDelay);
37683         }
37684     },
37685
37686     // private
37687     onKeyUp : function(e){
37688         if(!e.isNavKeyPress()){
37689             this.autoSize();
37690         }
37691     },
37692
37693     /**
37694      * Resets the current field value to the originally-loaded value and clears any validation messages.
37695      *  
37696      */
37697     reset : function(){
37698         Roo.form.TextField.superclass.reset.call(this);
37699        
37700     },
37701
37702     
37703     // private
37704     preFocus : function(){
37705         
37706         if(this.selectOnFocus){
37707             this.el.dom.select();
37708         }
37709     },
37710
37711     
37712     // private
37713     filterKeys : function(e){
37714         var k = e.getKey();
37715         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37716             return;
37717         }
37718         var c = e.getCharCode(), cc = String.fromCharCode(c);
37719         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37720             return;
37721         }
37722         if(!this.maskRe.test(cc)){
37723             e.stopEvent();
37724         }
37725     },
37726
37727     setValue : function(v){
37728         
37729         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37730         
37731         this.autoSize();
37732     },
37733
37734     /**
37735      * Validates a value according to the field's validation rules and marks the field as invalid
37736      * if the validation fails
37737      * @param {Mixed} value The value to validate
37738      * @return {Boolean} True if the value is valid, else false
37739      */
37740     validateValue : function(value){
37741         if(value.length < 1)  { // if it's blank
37742              if(this.allowBlank){
37743                 this.clearInvalid();
37744                 return true;
37745              }else{
37746                 this.markInvalid(this.blankText);
37747                 return false;
37748              }
37749         }
37750         if(value.length < this.minLength){
37751             this.markInvalid(String.format(this.minLengthText, this.minLength));
37752             return false;
37753         }
37754         if(value.length > this.maxLength){
37755             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37756             return false;
37757         }
37758         if(this.vtype){
37759             var vt = Roo.form.VTypes;
37760             if(!vt[this.vtype](value, this)){
37761                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37762                 return false;
37763             }
37764         }
37765         if(typeof this.validator == "function"){
37766             var msg = this.validator(value);
37767             if(msg !== true){
37768                 this.markInvalid(msg);
37769                 return false;
37770             }
37771         }
37772         if(this.regex && !this.regex.test(value)){
37773             this.markInvalid(this.regexText);
37774             return false;
37775         }
37776         return true;
37777     },
37778
37779     /**
37780      * Selects text in this field
37781      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37782      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37783      */
37784     selectText : function(start, end){
37785         var v = this.getRawValue();
37786         if(v.length > 0){
37787             start = start === undefined ? 0 : start;
37788             end = end === undefined ? v.length : end;
37789             var d = this.el.dom;
37790             if(d.setSelectionRange){
37791                 d.setSelectionRange(start, end);
37792             }else if(d.createTextRange){
37793                 var range = d.createTextRange();
37794                 range.moveStart("character", start);
37795                 range.moveEnd("character", v.length-end);
37796                 range.select();
37797             }
37798         }
37799     },
37800
37801     /**
37802      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37803      * This only takes effect if grow = true, and fires the autosize event.
37804      */
37805     autoSize : function(){
37806         if(!this.grow || !this.rendered){
37807             return;
37808         }
37809         if(!this.metrics){
37810             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37811         }
37812         var el = this.el;
37813         var v = el.dom.value;
37814         var d = document.createElement('div');
37815         d.appendChild(document.createTextNode(v));
37816         v = d.innerHTML;
37817         d = null;
37818         v += "&#160;";
37819         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37820         this.el.setWidth(w);
37821         this.fireEvent("autosize", this, w);
37822     },
37823     
37824     // private
37825     SafariOnKeyDown : function(event)
37826     {
37827         // this is a workaround for a password hang bug on chrome/ webkit.
37828         
37829         var isSelectAll = false;
37830         
37831         if(this.el.dom.selectionEnd > 0){
37832             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37833         }
37834         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37835             event.preventDefault();
37836             this.setValue('');
37837             return;
37838         }
37839         
37840         if(isSelectAll){ // backspace and delete key
37841             
37842             event.preventDefault();
37843             // this is very hacky as keydown always get's upper case.
37844             //
37845             var cc = String.fromCharCode(event.getCharCode());
37846             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37847             
37848         }
37849         
37850         
37851     }
37852 });/*
37853  * Based on:
37854  * Ext JS Library 1.1.1
37855  * Copyright(c) 2006-2007, Ext JS, LLC.
37856  *
37857  * Originally Released Under LGPL - original licence link has changed is not relivant.
37858  *
37859  * Fork - LGPL
37860  * <script type="text/javascript">
37861  */
37862  
37863 /**
37864  * @class Roo.form.Hidden
37865  * @extends Roo.form.TextField
37866  * Simple Hidden element used on forms 
37867  * 
37868  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37869  * 
37870  * @constructor
37871  * Creates a new Hidden form element.
37872  * @param {Object} config Configuration options
37873  */
37874
37875
37876
37877 // easy hidden field...
37878 Roo.form.Hidden = function(config){
37879     Roo.form.Hidden.superclass.constructor.call(this, config);
37880 };
37881   
37882 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37883     fieldLabel:      '',
37884     inputType:      'hidden',
37885     width:          50,
37886     allowBlank:     true,
37887     labelSeparator: '',
37888     hidden:         true,
37889     itemCls :       'x-form-item-display-none'
37890
37891
37892 });
37893
37894
37895 /*
37896  * Based on:
37897  * Ext JS Library 1.1.1
37898  * Copyright(c) 2006-2007, Ext JS, LLC.
37899  *
37900  * Originally Released Under LGPL - original licence link has changed is not relivant.
37901  *
37902  * Fork - LGPL
37903  * <script type="text/javascript">
37904  */
37905  
37906 /**
37907  * @class Roo.form.TriggerField
37908  * @extends Roo.form.TextField
37909  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37910  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37911  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37912  * for which you can provide a custom implementation.  For example:
37913  * <pre><code>
37914 var trigger = new Roo.form.TriggerField();
37915 trigger.onTriggerClick = myTriggerFn;
37916 trigger.applyTo('my-field');
37917 </code></pre>
37918  *
37919  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37920  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37921  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37922  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37923  * @constructor
37924  * Create a new TriggerField.
37925  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37926  * to the base TextField)
37927  */
37928 Roo.form.TriggerField = function(config){
37929     this.mimicing = false;
37930     Roo.form.TriggerField.superclass.constructor.call(this, config);
37931 };
37932
37933 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37934     /**
37935      * @cfg {String} triggerClass A CSS class to apply to the trigger
37936      */
37937     /**
37938      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37939      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37940      */
37941     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37942     /**
37943      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37944      */
37945     hideTrigger:false,
37946
37947     /** @cfg {Boolean} grow @hide */
37948     /** @cfg {Number} growMin @hide */
37949     /** @cfg {Number} growMax @hide */
37950
37951     /**
37952      * @hide 
37953      * @method
37954      */
37955     autoSize: Roo.emptyFn,
37956     // private
37957     monitorTab : true,
37958     // private
37959     deferHeight : true,
37960
37961     
37962     actionMode : 'wrap',
37963     // private
37964     onResize : function(w, h){
37965         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37966         if(typeof w == 'number'){
37967             var x = w - this.trigger.getWidth();
37968             this.el.setWidth(this.adjustWidth('input', x));
37969             this.trigger.setStyle('left', x+'px');
37970         }
37971     },
37972
37973     // private
37974     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37975
37976     // private
37977     getResizeEl : function(){
37978         return this.wrap;
37979     },
37980
37981     // private
37982     getPositionEl : function(){
37983         return this.wrap;
37984     },
37985
37986     // private
37987     alignErrorIcon : function(){
37988         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37989     },
37990
37991     // private
37992     onRender : function(ct, position){
37993         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37994         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37995         this.trigger = this.wrap.createChild(this.triggerConfig ||
37996                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37997         if(this.hideTrigger){
37998             this.trigger.setDisplayed(false);
37999         }
38000         this.initTrigger();
38001         if(!this.width){
38002             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38003         }
38004     },
38005
38006     // private
38007     initTrigger : function(){
38008         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38009         this.trigger.addClassOnOver('x-form-trigger-over');
38010         this.trigger.addClassOnClick('x-form-trigger-click');
38011     },
38012
38013     // private
38014     onDestroy : function(){
38015         if(this.trigger){
38016             this.trigger.removeAllListeners();
38017             this.trigger.remove();
38018         }
38019         if(this.wrap){
38020             this.wrap.remove();
38021         }
38022         Roo.form.TriggerField.superclass.onDestroy.call(this);
38023     },
38024
38025     // private
38026     onFocus : function(){
38027         Roo.form.TriggerField.superclass.onFocus.call(this);
38028         if(!this.mimicing){
38029             this.wrap.addClass('x-trigger-wrap-focus');
38030             this.mimicing = true;
38031             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38032             if(this.monitorTab){
38033                 this.el.on("keydown", this.checkTab, this);
38034             }
38035         }
38036     },
38037
38038     // private
38039     checkTab : function(e){
38040         if(e.getKey() == e.TAB){
38041             this.triggerBlur();
38042         }
38043     },
38044
38045     // private
38046     onBlur : function(){
38047         // do nothing
38048     },
38049
38050     // private
38051     mimicBlur : function(e, t){
38052         if(!this.wrap.contains(t) && this.validateBlur()){
38053             this.triggerBlur();
38054         }
38055     },
38056
38057     // private
38058     triggerBlur : function(){
38059         this.mimicing = false;
38060         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38061         if(this.monitorTab){
38062             this.el.un("keydown", this.checkTab, this);
38063         }
38064         this.wrap.removeClass('x-trigger-wrap-focus');
38065         Roo.form.TriggerField.superclass.onBlur.call(this);
38066     },
38067
38068     // private
38069     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38070     validateBlur : function(e, t){
38071         return true;
38072     },
38073
38074     // private
38075     onDisable : function(){
38076         Roo.form.TriggerField.superclass.onDisable.call(this);
38077         if(this.wrap){
38078             this.wrap.addClass('x-item-disabled');
38079         }
38080     },
38081
38082     // private
38083     onEnable : function(){
38084         Roo.form.TriggerField.superclass.onEnable.call(this);
38085         if(this.wrap){
38086             this.wrap.removeClass('x-item-disabled');
38087         }
38088     },
38089
38090     // private
38091     onShow : function(){
38092         var ae = this.getActionEl();
38093         
38094         if(ae){
38095             ae.dom.style.display = '';
38096             ae.dom.style.visibility = 'visible';
38097         }
38098     },
38099
38100     // private
38101     
38102     onHide : function(){
38103         var ae = this.getActionEl();
38104         ae.dom.style.display = 'none';
38105     },
38106
38107     /**
38108      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38109      * by an implementing function.
38110      * @method
38111      * @param {EventObject} e
38112      */
38113     onTriggerClick : Roo.emptyFn
38114 });
38115
38116 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38117 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38118 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38119 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38120     initComponent : function(){
38121         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38122
38123         this.triggerConfig = {
38124             tag:'span', cls:'x-form-twin-triggers', cn:[
38125             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38126             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38127         ]};
38128     },
38129
38130     getTrigger : function(index){
38131         return this.triggers[index];
38132     },
38133
38134     initTrigger : function(){
38135         var ts = this.trigger.select('.x-form-trigger', true);
38136         this.wrap.setStyle('overflow', 'hidden');
38137         var triggerField = this;
38138         ts.each(function(t, all, index){
38139             t.hide = function(){
38140                 var w = triggerField.wrap.getWidth();
38141                 this.dom.style.display = 'none';
38142                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38143             };
38144             t.show = function(){
38145                 var w = triggerField.wrap.getWidth();
38146                 this.dom.style.display = '';
38147                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38148             };
38149             var triggerIndex = 'Trigger'+(index+1);
38150
38151             if(this['hide'+triggerIndex]){
38152                 t.dom.style.display = 'none';
38153             }
38154             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38155             t.addClassOnOver('x-form-trigger-over');
38156             t.addClassOnClick('x-form-trigger-click');
38157         }, this);
38158         this.triggers = ts.elements;
38159     },
38160
38161     onTrigger1Click : Roo.emptyFn,
38162     onTrigger2Click : Roo.emptyFn
38163 });/*
38164  * Based on:
38165  * Ext JS Library 1.1.1
38166  * Copyright(c) 2006-2007, Ext JS, LLC.
38167  *
38168  * Originally Released Under LGPL - original licence link has changed is not relivant.
38169  *
38170  * Fork - LGPL
38171  * <script type="text/javascript">
38172  */
38173  
38174 /**
38175  * @class Roo.form.TextArea
38176  * @extends Roo.form.TextField
38177  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38178  * support for auto-sizing.
38179  * @constructor
38180  * Creates a new TextArea
38181  * @param {Object} config Configuration options
38182  */
38183 Roo.form.TextArea = function(config){
38184     Roo.form.TextArea.superclass.constructor.call(this, config);
38185     // these are provided exchanges for backwards compat
38186     // minHeight/maxHeight were replaced by growMin/growMax to be
38187     // compatible with TextField growing config values
38188     if(this.minHeight !== undefined){
38189         this.growMin = this.minHeight;
38190     }
38191     if(this.maxHeight !== undefined){
38192         this.growMax = this.maxHeight;
38193     }
38194 };
38195
38196 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38197     /**
38198      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38199      */
38200     growMin : 60,
38201     /**
38202      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38203      */
38204     growMax: 1000,
38205     /**
38206      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38207      * in the field (equivalent to setting overflow: hidden, defaults to false)
38208      */
38209     preventScrollbars: false,
38210     /**
38211      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38212      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38213      */
38214
38215     // private
38216     onRender : function(ct, position){
38217         if(!this.el){
38218             this.defaultAutoCreate = {
38219                 tag: "textarea",
38220                 style:"width:300px;height:60px;",
38221                 autocomplete: "off"
38222             };
38223         }
38224         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38225         if(this.grow){
38226             this.textSizeEl = Roo.DomHelper.append(document.body, {
38227                 tag: "pre", cls: "x-form-grow-sizer"
38228             });
38229             if(this.preventScrollbars){
38230                 this.el.setStyle("overflow", "hidden");
38231             }
38232             this.el.setHeight(this.growMin);
38233         }
38234     },
38235
38236     onDestroy : function(){
38237         if(this.textSizeEl){
38238             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38239         }
38240         Roo.form.TextArea.superclass.onDestroy.call(this);
38241     },
38242
38243     // private
38244     onKeyUp : function(e){
38245         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38246             this.autoSize();
38247         }
38248     },
38249
38250     /**
38251      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38252      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38253      */
38254     autoSize : function(){
38255         if(!this.grow || !this.textSizeEl){
38256             return;
38257         }
38258         var el = this.el;
38259         var v = el.dom.value;
38260         var ts = this.textSizeEl;
38261
38262         ts.innerHTML = '';
38263         ts.appendChild(document.createTextNode(v));
38264         v = ts.innerHTML;
38265
38266         Roo.fly(ts).setWidth(this.el.getWidth());
38267         if(v.length < 1){
38268             v = "&#160;&#160;";
38269         }else{
38270             if(Roo.isIE){
38271                 v = v.replace(/\n/g, '<p>&#160;</p>');
38272             }
38273             v += "&#160;\n&#160;";
38274         }
38275         ts.innerHTML = v;
38276         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38277         if(h != this.lastHeight){
38278             this.lastHeight = h;
38279             this.el.setHeight(h);
38280             this.fireEvent("autosize", this, h);
38281         }
38282     }
38283 });/*
38284  * Based on:
38285  * Ext JS Library 1.1.1
38286  * Copyright(c) 2006-2007, Ext JS, LLC.
38287  *
38288  * Originally Released Under LGPL - original licence link has changed is not relivant.
38289  *
38290  * Fork - LGPL
38291  * <script type="text/javascript">
38292  */
38293  
38294
38295 /**
38296  * @class Roo.form.NumberField
38297  * @extends Roo.form.TextField
38298  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38299  * @constructor
38300  * Creates a new NumberField
38301  * @param {Object} config Configuration options
38302  */
38303 Roo.form.NumberField = function(config){
38304     Roo.form.NumberField.superclass.constructor.call(this, config);
38305 };
38306
38307 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38308     /**
38309      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38310      */
38311     fieldClass: "x-form-field x-form-num-field",
38312     /**
38313      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38314      */
38315     allowDecimals : true,
38316     /**
38317      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38318      */
38319     decimalSeparator : ".",
38320     /**
38321      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38322      */
38323     decimalPrecision : 2,
38324     /**
38325      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38326      */
38327     allowNegative : true,
38328     /**
38329      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38330      */
38331     minValue : Number.NEGATIVE_INFINITY,
38332     /**
38333      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38334      */
38335     maxValue : Number.MAX_VALUE,
38336     /**
38337      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38338      */
38339     minText : "The minimum value for this field is {0}",
38340     /**
38341      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38342      */
38343     maxText : "The maximum value for this field is {0}",
38344     /**
38345      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38346      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38347      */
38348     nanText : "{0} is not a valid number",
38349
38350     // private
38351     initEvents : function(){
38352         Roo.form.NumberField.superclass.initEvents.call(this);
38353         var allowed = "0123456789";
38354         if(this.allowDecimals){
38355             allowed += this.decimalSeparator;
38356         }
38357         if(this.allowNegative){
38358             allowed += "-";
38359         }
38360         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38361         var keyPress = function(e){
38362             var k = e.getKey();
38363             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38364                 return;
38365             }
38366             var c = e.getCharCode();
38367             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38368                 e.stopEvent();
38369             }
38370         };
38371         this.el.on("keypress", keyPress, this);
38372     },
38373
38374     // private
38375     validateValue : function(value){
38376         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38377             return false;
38378         }
38379         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38380              return true;
38381         }
38382         var num = this.parseValue(value);
38383         if(isNaN(num)){
38384             this.markInvalid(String.format(this.nanText, value));
38385             return false;
38386         }
38387         if(num < this.minValue){
38388             this.markInvalid(String.format(this.minText, this.minValue));
38389             return false;
38390         }
38391         if(num > this.maxValue){
38392             this.markInvalid(String.format(this.maxText, this.maxValue));
38393             return false;
38394         }
38395         return true;
38396     },
38397
38398     getValue : function(){
38399         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38400     },
38401
38402     // private
38403     parseValue : function(value){
38404         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38405         return isNaN(value) ? '' : value;
38406     },
38407
38408     // private
38409     fixPrecision : function(value){
38410         var nan = isNaN(value);
38411         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38412             return nan ? '' : value;
38413         }
38414         return parseFloat(value).toFixed(this.decimalPrecision);
38415     },
38416
38417     setValue : function(v){
38418         v = this.fixPrecision(v);
38419         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38420     },
38421
38422     // private
38423     decimalPrecisionFcn : function(v){
38424         return Math.floor(v);
38425     },
38426
38427     beforeBlur : function(){
38428         var v = this.parseValue(this.getRawValue());
38429         if(v){
38430             this.setValue(v);
38431         }
38432     }
38433 });/*
38434  * Based on:
38435  * Ext JS Library 1.1.1
38436  * Copyright(c) 2006-2007, Ext JS, LLC.
38437  *
38438  * Originally Released Under LGPL - original licence link has changed is not relivant.
38439  *
38440  * Fork - LGPL
38441  * <script type="text/javascript">
38442  */
38443  
38444 /**
38445  * @class Roo.form.DateField
38446  * @extends Roo.form.TriggerField
38447  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38448 * @constructor
38449 * Create a new DateField
38450 * @param {Object} config
38451  */
38452 Roo.form.DateField = function(config){
38453     Roo.form.DateField.superclass.constructor.call(this, config);
38454     
38455       this.addEvents({
38456          
38457         /**
38458          * @event select
38459          * Fires when a date is selected
38460              * @param {Roo.form.DateField} combo This combo box
38461              * @param {Date} date The date selected
38462              */
38463         'select' : true
38464          
38465     });
38466     
38467     
38468     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38469     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38470     this.ddMatch = null;
38471     if(this.disabledDates){
38472         var dd = this.disabledDates;
38473         var re = "(?:";
38474         for(var i = 0; i < dd.length; i++){
38475             re += dd[i];
38476             if(i != dd.length-1) re += "|";
38477         }
38478         this.ddMatch = new RegExp(re + ")");
38479     }
38480 };
38481
38482 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38483     /**
38484      * @cfg {String} format
38485      * The default date format string which can be overriden for localization support.  The format must be
38486      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38487      */
38488     format : "m/d/y",
38489     /**
38490      * @cfg {String} altFormats
38491      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38492      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38493      */
38494     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38495     /**
38496      * @cfg {Array} disabledDays
38497      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38498      */
38499     disabledDays : null,
38500     /**
38501      * @cfg {String} disabledDaysText
38502      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38503      */
38504     disabledDaysText : "Disabled",
38505     /**
38506      * @cfg {Array} disabledDates
38507      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38508      * expression so they are very powerful. Some examples:
38509      * <ul>
38510      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38511      * <li>["03/08", "09/16"] would disable those days for every year</li>
38512      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38513      * <li>["03/../2006"] would disable every day in March 2006</li>
38514      * <li>["^03"] would disable every day in every March</li>
38515      * </ul>
38516      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38517      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38518      */
38519     disabledDates : null,
38520     /**
38521      * @cfg {String} disabledDatesText
38522      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38523      */
38524     disabledDatesText : "Disabled",
38525     /**
38526      * @cfg {Date/String} minValue
38527      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38528      * valid format (defaults to null).
38529      */
38530     minValue : null,
38531     /**
38532      * @cfg {Date/String} maxValue
38533      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38534      * valid format (defaults to null).
38535      */
38536     maxValue : null,
38537     /**
38538      * @cfg {String} minText
38539      * The error text to display when the date in the cell is before minValue (defaults to
38540      * 'The date in this field must be after {minValue}').
38541      */
38542     minText : "The date in this field must be equal to or after {0}",
38543     /**
38544      * @cfg {String} maxText
38545      * The error text to display when the date in the cell is after maxValue (defaults to
38546      * 'The date in this field must be before {maxValue}').
38547      */
38548     maxText : "The date in this field must be equal to or before {0}",
38549     /**
38550      * @cfg {String} invalidText
38551      * The error text to display when the date in the field is invalid (defaults to
38552      * '{value} is not a valid date - it must be in the format {format}').
38553      */
38554     invalidText : "{0} is not a valid date - it must be in the format {1}",
38555     /**
38556      * @cfg {String} triggerClass
38557      * An additional CSS class used to style the trigger button.  The trigger will always get the
38558      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38559      * which displays a calendar icon).
38560      */
38561     triggerClass : 'x-form-date-trigger',
38562     
38563
38564     /**
38565      * @cfg {Boolean} useIso
38566      * if enabled, then the date field will use a hidden field to store the 
38567      * real value as iso formated date. default (false)
38568      */ 
38569     useIso : false,
38570     /**
38571      * @cfg {String/Object} autoCreate
38572      * A DomHelper element spec, or true for a default element spec (defaults to
38573      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38574      */ 
38575     // private
38576     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38577     
38578     // private
38579     hiddenField: false,
38580     
38581     onRender : function(ct, position)
38582     {
38583         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38584         if (this.useIso) {
38585             //this.el.dom.removeAttribute('name'); 
38586             Roo.log("Changing name?");
38587             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38588             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38589                     'before', true);
38590             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38591             // prevent input submission
38592             this.hiddenName = this.name;
38593         }
38594             
38595             
38596     },
38597     
38598     // private
38599     validateValue : function(value)
38600     {
38601         value = this.formatDate(value);
38602         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38603             Roo.log('super failed');
38604             return false;
38605         }
38606         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38607              return true;
38608         }
38609         var svalue = value;
38610         value = this.parseDate(value);
38611         if(!value){
38612             Roo.log('parse date failed' + svalue);
38613             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38614             return false;
38615         }
38616         var time = value.getTime();
38617         if(this.minValue && time < this.minValue.getTime()){
38618             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38619             return false;
38620         }
38621         if(this.maxValue && time > this.maxValue.getTime()){
38622             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38623             return false;
38624         }
38625         if(this.disabledDays){
38626             var day = value.getDay();
38627             for(var i = 0; i < this.disabledDays.length; i++) {
38628                 if(day === this.disabledDays[i]){
38629                     this.markInvalid(this.disabledDaysText);
38630                     return false;
38631                 }
38632             }
38633         }
38634         var fvalue = this.formatDate(value);
38635         if(this.ddMatch && this.ddMatch.test(fvalue)){
38636             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38637             return false;
38638         }
38639         return true;
38640     },
38641
38642     // private
38643     // Provides logic to override the default TriggerField.validateBlur which just returns true
38644     validateBlur : function(){
38645         return !this.menu || !this.menu.isVisible();
38646     },
38647     
38648     getName: function()
38649     {
38650         // returns hidden if it's set..
38651         if (!this.rendered) {return ''};
38652         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38653         
38654     },
38655
38656     /**
38657      * Returns the current date value of the date field.
38658      * @return {Date} The date value
38659      */
38660     getValue : function(){
38661         
38662         return  this.hiddenField ?
38663                 this.hiddenField.value :
38664                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38665     },
38666
38667     /**
38668      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38669      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38670      * (the default format used is "m/d/y").
38671      * <br />Usage:
38672      * <pre><code>
38673 //All of these calls set the same date value (May 4, 2006)
38674
38675 //Pass a date object:
38676 var dt = new Date('5/4/06');
38677 dateField.setValue(dt);
38678
38679 //Pass a date string (default format):
38680 dateField.setValue('5/4/06');
38681
38682 //Pass a date string (custom format):
38683 dateField.format = 'Y-m-d';
38684 dateField.setValue('2006-5-4');
38685 </code></pre>
38686      * @param {String/Date} date The date or valid date string
38687      */
38688     setValue : function(date){
38689         if (this.hiddenField) {
38690             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38691         }
38692         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38693         // make sure the value field is always stored as a date..
38694         this.value = this.parseDate(date);
38695         
38696         
38697     },
38698
38699     // private
38700     parseDate : function(value){
38701         if(!value || value instanceof Date){
38702             return value;
38703         }
38704         var v = Date.parseDate(value, this.format);
38705          if (!v && this.useIso) {
38706             v = Date.parseDate(value, 'Y-m-d');
38707         }
38708         if(!v && this.altFormats){
38709             if(!this.altFormatsArray){
38710                 this.altFormatsArray = this.altFormats.split("|");
38711             }
38712             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38713                 v = Date.parseDate(value, this.altFormatsArray[i]);
38714             }
38715         }
38716         return v;
38717     },
38718
38719     // private
38720     formatDate : function(date, fmt){
38721         return (!date || !(date instanceof Date)) ?
38722                date : date.dateFormat(fmt || this.format);
38723     },
38724
38725     // private
38726     menuListeners : {
38727         select: function(m, d){
38728             
38729             this.setValue(d);
38730             this.fireEvent('select', this, d);
38731         },
38732         show : function(){ // retain focus styling
38733             this.onFocus();
38734         },
38735         hide : function(){
38736             this.focus.defer(10, this);
38737             var ml = this.menuListeners;
38738             this.menu.un("select", ml.select,  this);
38739             this.menu.un("show", ml.show,  this);
38740             this.menu.un("hide", ml.hide,  this);
38741         }
38742     },
38743
38744     // private
38745     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38746     onTriggerClick : function(){
38747         if(this.disabled){
38748             return;
38749         }
38750         if(this.menu == null){
38751             this.menu = new Roo.menu.DateMenu();
38752         }
38753         Roo.apply(this.menu.picker,  {
38754             showClear: this.allowBlank,
38755             minDate : this.minValue,
38756             maxDate : this.maxValue,
38757             disabledDatesRE : this.ddMatch,
38758             disabledDatesText : this.disabledDatesText,
38759             disabledDays : this.disabledDays,
38760             disabledDaysText : this.disabledDaysText,
38761             format : this.useIso ? 'Y-m-d' : this.format,
38762             minText : String.format(this.minText, this.formatDate(this.minValue)),
38763             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38764         });
38765         this.menu.on(Roo.apply({}, this.menuListeners, {
38766             scope:this
38767         }));
38768         this.menu.picker.setValue(this.getValue() || new Date());
38769         this.menu.show(this.el, "tl-bl?");
38770     },
38771
38772     beforeBlur : function(){
38773         var v = this.parseDate(this.getRawValue());
38774         if(v){
38775             this.setValue(v);
38776         }
38777     },
38778
38779     /*@
38780      * overide
38781      * 
38782      */
38783     isDirty : function() {
38784         if(this.disabled) {
38785             return false;
38786         }
38787         
38788         if(typeof(this.startValue) === 'undefined'){
38789             return false;
38790         }
38791         
38792         return String(this.getValue()) !== String(this.startValue);
38793         
38794     }
38795 });/*
38796  * Based on:
38797  * Ext JS Library 1.1.1
38798  * Copyright(c) 2006-2007, Ext JS, LLC.
38799  *
38800  * Originally Released Under LGPL - original licence link has changed is not relivant.
38801  *
38802  * Fork - LGPL
38803  * <script type="text/javascript">
38804  */
38805  
38806 /**
38807  * @class Roo.form.MonthField
38808  * @extends Roo.form.TriggerField
38809  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38810 * @constructor
38811 * Create a new MonthField
38812 * @param {Object} config
38813  */
38814 Roo.form.MonthField = function(config){
38815     
38816     Roo.form.MonthField.superclass.constructor.call(this, config);
38817     
38818       this.addEvents({
38819          
38820         /**
38821          * @event select
38822          * Fires when a date is selected
38823              * @param {Roo.form.MonthFieeld} combo This combo box
38824              * @param {Date} date The date selected
38825              */
38826         'select' : true
38827          
38828     });
38829     
38830     
38831     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38832     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38833     this.ddMatch = null;
38834     if(this.disabledDates){
38835         var dd = this.disabledDates;
38836         var re = "(?:";
38837         for(var i = 0; i < dd.length; i++){
38838             re += dd[i];
38839             if(i != dd.length-1) re += "|";
38840         }
38841         this.ddMatch = new RegExp(re + ")");
38842     }
38843 };
38844
38845 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38846     /**
38847      * @cfg {String} format
38848      * The default date format string which can be overriden for localization support.  The format must be
38849      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38850      */
38851     format : "M Y",
38852     /**
38853      * @cfg {String} altFormats
38854      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38855      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38856      */
38857     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38858     /**
38859      * @cfg {Array} disabledDays
38860      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38861      */
38862     disabledDays : [0,1,2,3,4,5,6],
38863     /**
38864      * @cfg {String} disabledDaysText
38865      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38866      */
38867     disabledDaysText : "Disabled",
38868     /**
38869      * @cfg {Array} disabledDates
38870      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38871      * expression so they are very powerful. Some examples:
38872      * <ul>
38873      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38874      * <li>["03/08", "09/16"] would disable those days for every year</li>
38875      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38876      * <li>["03/../2006"] would disable every day in March 2006</li>
38877      * <li>["^03"] would disable every day in every March</li>
38878      * </ul>
38879      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38880      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38881      */
38882     disabledDates : null,
38883     /**
38884      * @cfg {String} disabledDatesText
38885      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38886      */
38887     disabledDatesText : "Disabled",
38888     /**
38889      * @cfg {Date/String} minValue
38890      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38891      * valid format (defaults to null).
38892      */
38893     minValue : null,
38894     /**
38895      * @cfg {Date/String} maxValue
38896      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38897      * valid format (defaults to null).
38898      */
38899     maxValue : null,
38900     /**
38901      * @cfg {String} minText
38902      * The error text to display when the date in the cell is before minValue (defaults to
38903      * 'The date in this field must be after {minValue}').
38904      */
38905     minText : "The date in this field must be equal to or after {0}",
38906     /**
38907      * @cfg {String} maxTextf
38908      * The error text to display when the date in the cell is after maxValue (defaults to
38909      * 'The date in this field must be before {maxValue}').
38910      */
38911     maxText : "The date in this field must be equal to or before {0}",
38912     /**
38913      * @cfg {String} invalidText
38914      * The error text to display when the date in the field is invalid (defaults to
38915      * '{value} is not a valid date - it must be in the format {format}').
38916      */
38917     invalidText : "{0} is not a valid date - it must be in the format {1}",
38918     /**
38919      * @cfg {String} triggerClass
38920      * An additional CSS class used to style the trigger button.  The trigger will always get the
38921      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38922      * which displays a calendar icon).
38923      */
38924     triggerClass : 'x-form-date-trigger',
38925     
38926
38927     /**
38928      * @cfg {Boolean} useIso
38929      * if enabled, then the date field will use a hidden field to store the 
38930      * real value as iso formated date. default (true)
38931      */ 
38932     useIso : true,
38933     /**
38934      * @cfg {String/Object} autoCreate
38935      * A DomHelper element spec, or true for a default element spec (defaults to
38936      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38937      */ 
38938     // private
38939     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38940     
38941     // private
38942     hiddenField: false,
38943     
38944     hideMonthPicker : false,
38945     
38946     onRender : function(ct, position)
38947     {
38948         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38949         if (this.useIso) {
38950             this.el.dom.removeAttribute('name'); 
38951             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38952                     'before', true);
38953             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38954             // prevent input submission
38955             this.hiddenName = this.name;
38956         }
38957             
38958             
38959     },
38960     
38961     // private
38962     validateValue : function(value)
38963     {
38964         value = this.formatDate(value);
38965         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38966             return false;
38967         }
38968         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38969              return true;
38970         }
38971         var svalue = value;
38972         value = this.parseDate(value);
38973         if(!value){
38974             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38975             return false;
38976         }
38977         var time = value.getTime();
38978         if(this.minValue && time < this.minValue.getTime()){
38979             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38980             return false;
38981         }
38982         if(this.maxValue && time > this.maxValue.getTime()){
38983             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38984             return false;
38985         }
38986         /*if(this.disabledDays){
38987             var day = value.getDay();
38988             for(var i = 0; i < this.disabledDays.length; i++) {
38989                 if(day === this.disabledDays[i]){
38990                     this.markInvalid(this.disabledDaysText);
38991                     return false;
38992                 }
38993             }
38994         }
38995         */
38996         var fvalue = this.formatDate(value);
38997         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38998             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38999             return false;
39000         }
39001         */
39002         return true;
39003     },
39004
39005     // private
39006     // Provides logic to override the default TriggerField.validateBlur which just returns true
39007     validateBlur : function(){
39008         return !this.menu || !this.menu.isVisible();
39009     },
39010
39011     /**
39012      * Returns the current date value of the date field.
39013      * @return {Date} The date value
39014      */
39015     getValue : function(){
39016         
39017         
39018         
39019         return  this.hiddenField ?
39020                 this.hiddenField.value :
39021                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39022     },
39023
39024     /**
39025      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39026      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39027      * (the default format used is "m/d/y").
39028      * <br />Usage:
39029      * <pre><code>
39030 //All of these calls set the same date value (May 4, 2006)
39031
39032 //Pass a date object:
39033 var dt = new Date('5/4/06');
39034 monthField.setValue(dt);
39035
39036 //Pass a date string (default format):
39037 monthField.setValue('5/4/06');
39038
39039 //Pass a date string (custom format):
39040 monthField.format = 'Y-m-d';
39041 monthField.setValue('2006-5-4');
39042 </code></pre>
39043      * @param {String/Date} date The date or valid date string
39044      */
39045     setValue : function(date){
39046         Roo.log('month setValue' + date);
39047         // can only be first of month..
39048         
39049         var val = this.parseDate(date);
39050         
39051         if (this.hiddenField) {
39052             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39053         }
39054         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39055         this.value = this.parseDate(date);
39056     },
39057
39058     // private
39059     parseDate : function(value){
39060         if(!value || value instanceof Date){
39061             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39062             return value;
39063         }
39064         var v = Date.parseDate(value, this.format);
39065         if (!v && this.useIso) {
39066             v = Date.parseDate(value, 'Y-m-d');
39067         }
39068         if (v) {
39069             // 
39070             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39071         }
39072         
39073         
39074         if(!v && this.altFormats){
39075             if(!this.altFormatsArray){
39076                 this.altFormatsArray = this.altFormats.split("|");
39077             }
39078             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39079                 v = Date.parseDate(value, this.altFormatsArray[i]);
39080             }
39081         }
39082         return v;
39083     },
39084
39085     // private
39086     formatDate : function(date, fmt){
39087         return (!date || !(date instanceof Date)) ?
39088                date : date.dateFormat(fmt || this.format);
39089     },
39090
39091     // private
39092     menuListeners : {
39093         select: function(m, d){
39094             this.setValue(d);
39095             this.fireEvent('select', this, d);
39096         },
39097         show : function(){ // retain focus styling
39098             this.onFocus();
39099         },
39100         hide : function(){
39101             this.focus.defer(10, this);
39102             var ml = this.menuListeners;
39103             this.menu.un("select", ml.select,  this);
39104             this.menu.un("show", ml.show,  this);
39105             this.menu.un("hide", ml.hide,  this);
39106         }
39107     },
39108     // private
39109     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39110     onTriggerClick : function(){
39111         if(this.disabled){
39112             return;
39113         }
39114         if(this.menu == null){
39115             this.menu = new Roo.menu.DateMenu();
39116            
39117         }
39118         
39119         Roo.apply(this.menu.picker,  {
39120             
39121             showClear: this.allowBlank,
39122             minDate : this.minValue,
39123             maxDate : this.maxValue,
39124             disabledDatesRE : this.ddMatch,
39125             disabledDatesText : this.disabledDatesText,
39126             
39127             format : this.useIso ? 'Y-m-d' : this.format,
39128             minText : String.format(this.minText, this.formatDate(this.minValue)),
39129             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39130             
39131         });
39132          this.menu.on(Roo.apply({}, this.menuListeners, {
39133             scope:this
39134         }));
39135        
39136         
39137         var m = this.menu;
39138         var p = m.picker;
39139         
39140         // hide month picker get's called when we called by 'before hide';
39141         
39142         var ignorehide = true;
39143         p.hideMonthPicker  = function(disableAnim){
39144             if (ignorehide) {
39145                 return;
39146             }
39147              if(this.monthPicker){
39148                 Roo.log("hideMonthPicker called");
39149                 if(disableAnim === true){
39150                     this.monthPicker.hide();
39151                 }else{
39152                     this.monthPicker.slideOut('t', {duration:.2});
39153                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39154                     p.fireEvent("select", this, this.value);
39155                     m.hide();
39156                 }
39157             }
39158         }
39159         
39160         Roo.log('picker set value');
39161         Roo.log(this.getValue());
39162         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39163         m.show(this.el, 'tl-bl?');
39164         ignorehide  = false;
39165         // this will trigger hideMonthPicker..
39166         
39167         
39168         // hidden the day picker
39169         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39170         
39171         
39172         
39173       
39174         
39175         p.showMonthPicker.defer(100, p);
39176     
39177         
39178        
39179     },
39180
39181     beforeBlur : function(){
39182         var v = this.parseDate(this.getRawValue());
39183         if(v){
39184             this.setValue(v);
39185         }
39186     }
39187
39188     /** @cfg {Boolean} grow @hide */
39189     /** @cfg {Number} growMin @hide */
39190     /** @cfg {Number} growMax @hide */
39191     /**
39192      * @hide
39193      * @method autoSize
39194      */
39195 });/*
39196  * Based on:
39197  * Ext JS Library 1.1.1
39198  * Copyright(c) 2006-2007, Ext JS, LLC.
39199  *
39200  * Originally Released Under LGPL - original licence link has changed is not relivant.
39201  *
39202  * Fork - LGPL
39203  * <script type="text/javascript">
39204  */
39205  
39206
39207 /**
39208  * @class Roo.form.ComboBox
39209  * @extends Roo.form.TriggerField
39210  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39211  * @constructor
39212  * Create a new ComboBox.
39213  * @param {Object} config Configuration options
39214  */
39215 Roo.form.ComboBox = function(config){
39216     Roo.form.ComboBox.superclass.constructor.call(this, config);
39217     this.addEvents({
39218         /**
39219          * @event expand
39220          * Fires when the dropdown list is expanded
39221              * @param {Roo.form.ComboBox} combo This combo box
39222              */
39223         'expand' : true,
39224         /**
39225          * @event collapse
39226          * Fires when the dropdown list is collapsed
39227              * @param {Roo.form.ComboBox} combo This combo box
39228              */
39229         'collapse' : true,
39230         /**
39231          * @event beforeselect
39232          * Fires before a list item is selected. Return false to cancel the selection.
39233              * @param {Roo.form.ComboBox} combo This combo box
39234              * @param {Roo.data.Record} record The data record returned from the underlying store
39235              * @param {Number} index The index of the selected item in the dropdown list
39236              */
39237         'beforeselect' : true,
39238         /**
39239          * @event select
39240          * Fires when a list item is selected
39241              * @param {Roo.form.ComboBox} combo This combo box
39242              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39243              * @param {Number} index The index of the selected item in the dropdown list
39244              */
39245         'select' : true,
39246         /**
39247          * @event beforequery
39248          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39249          * The event object passed has these properties:
39250              * @param {Roo.form.ComboBox} combo This combo box
39251              * @param {String} query The query
39252              * @param {Boolean} forceAll true to force "all" query
39253              * @param {Boolean} cancel true to cancel the query
39254              * @param {Object} e The query event object
39255              */
39256         'beforequery': true,
39257          /**
39258          * @event add
39259          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39260              * @param {Roo.form.ComboBox} combo This combo box
39261              */
39262         'add' : true,
39263         /**
39264          * @event edit
39265          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39266              * @param {Roo.form.ComboBox} combo This combo box
39267              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39268              */
39269         'edit' : true
39270         
39271         
39272     });
39273     if(this.transform){
39274         this.allowDomMove = false;
39275         var s = Roo.getDom(this.transform);
39276         if(!this.hiddenName){
39277             this.hiddenName = s.name;
39278         }
39279         if(!this.store){
39280             this.mode = 'local';
39281             var d = [], opts = s.options;
39282             for(var i = 0, len = opts.length;i < len; i++){
39283                 var o = opts[i];
39284                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39285                 if(o.selected) {
39286                     this.value = value;
39287                 }
39288                 d.push([value, o.text]);
39289             }
39290             this.store = new Roo.data.SimpleStore({
39291                 'id': 0,
39292                 fields: ['value', 'text'],
39293                 data : d
39294             });
39295             this.valueField = 'value';
39296             this.displayField = 'text';
39297         }
39298         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39299         if(!this.lazyRender){
39300             this.target = true;
39301             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39302             s.parentNode.removeChild(s); // remove it
39303             this.render(this.el.parentNode);
39304         }else{
39305             s.parentNode.removeChild(s); // remove it
39306         }
39307
39308     }
39309     if (this.store) {
39310         this.store = Roo.factory(this.store, Roo.data);
39311     }
39312     
39313     this.selectedIndex = -1;
39314     if(this.mode == 'local'){
39315         if(config.queryDelay === undefined){
39316             this.queryDelay = 10;
39317         }
39318         if(config.minChars === undefined){
39319             this.minChars = 0;
39320         }
39321     }
39322 };
39323
39324 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39325     /**
39326      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39327      */
39328     /**
39329      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39330      * rendering into an Roo.Editor, defaults to false)
39331      */
39332     /**
39333      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39334      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39335      */
39336     /**
39337      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39338      */
39339     /**
39340      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39341      * the dropdown list (defaults to undefined, with no header element)
39342      */
39343
39344      /**
39345      * @cfg {String/Roo.Template} tpl The template to use to render the output
39346      */
39347      
39348     // private
39349     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39350     /**
39351      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39352      */
39353     listWidth: undefined,
39354     /**
39355      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39356      * mode = 'remote' or 'text' if mode = 'local')
39357      */
39358     displayField: undefined,
39359     /**
39360      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39361      * mode = 'remote' or 'value' if mode = 'local'). 
39362      * Note: use of a valueField requires the user make a selection
39363      * in order for a value to be mapped.
39364      */
39365     valueField: undefined,
39366     
39367     
39368     /**
39369      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39370      * field's data value (defaults to the underlying DOM element's name)
39371      */
39372     hiddenName: undefined,
39373     /**
39374      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39375      */
39376     listClass: '',
39377     /**
39378      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39379      */
39380     selectedClass: 'x-combo-selected',
39381     /**
39382      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39383      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39384      * which displays a downward arrow icon).
39385      */
39386     triggerClass : 'x-form-arrow-trigger',
39387     /**
39388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39389      */
39390     shadow:'sides',
39391     /**
39392      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39393      * anchor positions (defaults to 'tl-bl')
39394      */
39395     listAlign: 'tl-bl?',
39396     /**
39397      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39398      */
39399     maxHeight: 300,
39400     /**
39401      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39402      * query specified by the allQuery config option (defaults to 'query')
39403      */
39404     triggerAction: 'query',
39405     /**
39406      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39407      * (defaults to 4, does not apply if editable = false)
39408      */
39409     minChars : 4,
39410     /**
39411      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39412      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39413      */
39414     typeAhead: false,
39415     /**
39416      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39417      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39418      */
39419     queryDelay: 500,
39420     /**
39421      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39422      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39423      */
39424     pageSize: 0,
39425     /**
39426      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39427      * when editable = true (defaults to false)
39428      */
39429     selectOnFocus:false,
39430     /**
39431      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39432      */
39433     queryParam: 'query',
39434     /**
39435      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39436      * when mode = 'remote' (defaults to 'Loading...')
39437      */
39438     loadingText: 'Loading...',
39439     /**
39440      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39441      */
39442     resizable: false,
39443     /**
39444      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39445      */
39446     handleHeight : 8,
39447     /**
39448      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39449      * traditional select (defaults to true)
39450      */
39451     editable: true,
39452     /**
39453      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39454      */
39455     allQuery: '',
39456     /**
39457      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39458      */
39459     mode: 'remote',
39460     /**
39461      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39462      * listWidth has a higher value)
39463      */
39464     minListWidth : 70,
39465     /**
39466      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39467      * allow the user to set arbitrary text into the field (defaults to false)
39468      */
39469     forceSelection:false,
39470     /**
39471      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39472      * if typeAhead = true (defaults to 250)
39473      */
39474     typeAheadDelay : 250,
39475     /**
39476      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39477      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39478      */
39479     valueNotFoundText : undefined,
39480     /**
39481      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39482      */
39483     blockFocus : false,
39484     
39485     /**
39486      * @cfg {Boolean} disableClear Disable showing of clear button.
39487      */
39488     disableClear : false,
39489     /**
39490      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39491      */
39492     alwaysQuery : false,
39493     
39494     //private
39495     addicon : false,
39496     editicon: false,
39497     
39498     // element that contains real text value.. (when hidden is used..)
39499      
39500     // private
39501     onRender : function(ct, position){
39502         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39503         if(this.hiddenName){
39504             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39505                     'before', true);
39506             this.hiddenField.value =
39507                 this.hiddenValue !== undefined ? this.hiddenValue :
39508                 this.value !== undefined ? this.value : '';
39509
39510             // prevent input submission
39511             this.el.dom.removeAttribute('name');
39512              
39513              
39514         }
39515         if(Roo.isGecko){
39516             this.el.dom.setAttribute('autocomplete', 'off');
39517         }
39518
39519         var cls = 'x-combo-list';
39520
39521         this.list = new Roo.Layer({
39522             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39523         });
39524
39525         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39526         this.list.setWidth(lw);
39527         this.list.swallowEvent('mousewheel');
39528         this.assetHeight = 0;
39529
39530         if(this.title){
39531             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39532             this.assetHeight += this.header.getHeight();
39533         }
39534
39535         this.innerList = this.list.createChild({cls:cls+'-inner'});
39536         this.innerList.on('mouseover', this.onViewOver, this);
39537         this.innerList.on('mousemove', this.onViewMove, this);
39538         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39539         
39540         if(this.allowBlank && !this.pageSize && !this.disableClear){
39541             this.footer = this.list.createChild({cls:cls+'-ft'});
39542             this.pageTb = new Roo.Toolbar(this.footer);
39543            
39544         }
39545         if(this.pageSize){
39546             this.footer = this.list.createChild({cls:cls+'-ft'});
39547             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39548                     {pageSize: this.pageSize});
39549             
39550         }
39551         
39552         if (this.pageTb && this.allowBlank && !this.disableClear) {
39553             var _this = this;
39554             this.pageTb.add(new Roo.Toolbar.Fill(), {
39555                 cls: 'x-btn-icon x-btn-clear',
39556                 text: '&#160;',
39557                 handler: function()
39558                 {
39559                     _this.collapse();
39560                     _this.clearValue();
39561                     _this.onSelect(false, -1);
39562                 }
39563             });
39564         }
39565         if (this.footer) {
39566             this.assetHeight += this.footer.getHeight();
39567         }
39568         
39569
39570         if(!this.tpl){
39571             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39572         }
39573
39574         this.view = new Roo.View(this.innerList, this.tpl, {
39575             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39576         });
39577
39578         this.view.on('click', this.onViewClick, this);
39579
39580         this.store.on('beforeload', this.onBeforeLoad, this);
39581         this.store.on('load', this.onLoad, this);
39582         this.store.on('loadexception', this.onLoadException, this);
39583
39584         if(this.resizable){
39585             this.resizer = new Roo.Resizable(this.list,  {
39586                pinned:true, handles:'se'
39587             });
39588             this.resizer.on('resize', function(r, w, h){
39589                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39590                 this.listWidth = w;
39591                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39592                 this.restrictHeight();
39593             }, this);
39594             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39595         }
39596         if(!this.editable){
39597             this.editable = true;
39598             this.setEditable(false);
39599         }  
39600         
39601         
39602         if (typeof(this.events.add.listeners) != 'undefined') {
39603             
39604             this.addicon = this.wrap.createChild(
39605                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39606        
39607             this.addicon.on('click', function(e) {
39608                 this.fireEvent('add', this);
39609             }, this);
39610         }
39611         if (typeof(this.events.edit.listeners) != 'undefined') {
39612             
39613             this.editicon = this.wrap.createChild(
39614                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39615             if (this.addicon) {
39616                 this.editicon.setStyle('margin-left', '40px');
39617             }
39618             this.editicon.on('click', function(e) {
39619                 
39620                 // we fire even  if inothing is selected..
39621                 this.fireEvent('edit', this, this.lastData );
39622                 
39623             }, this);
39624         }
39625         
39626         
39627         
39628     },
39629
39630     // private
39631     initEvents : function(){
39632         Roo.form.ComboBox.superclass.initEvents.call(this);
39633
39634         this.keyNav = new Roo.KeyNav(this.el, {
39635             "up" : function(e){
39636                 this.inKeyMode = true;
39637                 this.selectPrev();
39638             },
39639
39640             "down" : function(e){
39641                 if(!this.isExpanded()){
39642                     this.onTriggerClick();
39643                 }else{
39644                     this.inKeyMode = true;
39645                     this.selectNext();
39646                 }
39647             },
39648
39649             "enter" : function(e){
39650                 this.onViewClick();
39651                 //return true;
39652             },
39653
39654             "esc" : function(e){
39655                 this.collapse();
39656             },
39657
39658             "tab" : function(e){
39659                 this.onViewClick(false);
39660                 this.fireEvent("specialkey", this, e);
39661                 return true;
39662             },
39663
39664             scope : this,
39665
39666             doRelay : function(foo, bar, hname){
39667                 if(hname == 'down' || this.scope.isExpanded()){
39668                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39669                 }
39670                 return true;
39671             },
39672
39673             forceKeyDown: true
39674         });
39675         this.queryDelay = Math.max(this.queryDelay || 10,
39676                 this.mode == 'local' ? 10 : 250);
39677         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39678         if(this.typeAhead){
39679             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39680         }
39681         if(this.editable !== false){
39682             this.el.on("keyup", this.onKeyUp, this);
39683         }
39684         if(this.forceSelection){
39685             this.on('blur', this.doForce, this);
39686         }
39687     },
39688
39689     onDestroy : function(){
39690         if(this.view){
39691             this.view.setStore(null);
39692             this.view.el.removeAllListeners();
39693             this.view.el.remove();
39694             this.view.purgeListeners();
39695         }
39696         if(this.list){
39697             this.list.destroy();
39698         }
39699         if(this.store){
39700             this.store.un('beforeload', this.onBeforeLoad, this);
39701             this.store.un('load', this.onLoad, this);
39702             this.store.un('loadexception', this.onLoadException, this);
39703         }
39704         Roo.form.ComboBox.superclass.onDestroy.call(this);
39705     },
39706
39707     // private
39708     fireKey : function(e){
39709         if(e.isNavKeyPress() && !this.list.isVisible()){
39710             this.fireEvent("specialkey", this, e);
39711         }
39712     },
39713
39714     // private
39715     onResize: function(w, h){
39716         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39717         
39718         if(typeof w != 'number'){
39719             // we do not handle it!?!?
39720             return;
39721         }
39722         var tw = this.trigger.getWidth();
39723         tw += this.addicon ? this.addicon.getWidth() : 0;
39724         tw += this.editicon ? this.editicon.getWidth() : 0;
39725         var x = w - tw;
39726         this.el.setWidth( this.adjustWidth('input', x));
39727             
39728         this.trigger.setStyle('left', x+'px');
39729         
39730         if(this.list && this.listWidth === undefined){
39731             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39732             this.list.setWidth(lw);
39733             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39734         }
39735         
39736     
39737         
39738     },
39739
39740     /**
39741      * Allow or prevent the user from directly editing the field text.  If false is passed,
39742      * the user will only be able to select from the items defined in the dropdown list.  This method
39743      * is the runtime equivalent of setting the 'editable' config option at config time.
39744      * @param {Boolean} value True to allow the user to directly edit the field text
39745      */
39746     setEditable : function(value){
39747         if(value == this.editable){
39748             return;
39749         }
39750         this.editable = value;
39751         if(!value){
39752             this.el.dom.setAttribute('readOnly', true);
39753             this.el.on('mousedown', this.onTriggerClick,  this);
39754             this.el.addClass('x-combo-noedit');
39755         }else{
39756             this.el.dom.setAttribute('readOnly', false);
39757             this.el.un('mousedown', this.onTriggerClick,  this);
39758             this.el.removeClass('x-combo-noedit');
39759         }
39760     },
39761
39762     // private
39763     onBeforeLoad : function(){
39764         if(!this.hasFocus){
39765             return;
39766         }
39767         this.innerList.update(this.loadingText ?
39768                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39769         this.restrictHeight();
39770         this.selectedIndex = -1;
39771     },
39772
39773     // private
39774     onLoad : function(){
39775         if(!this.hasFocus){
39776             return;
39777         }
39778         if(this.store.getCount() > 0){
39779             this.expand();
39780             this.restrictHeight();
39781             if(this.lastQuery == this.allQuery){
39782                 if(this.editable){
39783                     this.el.dom.select();
39784                 }
39785                 if(!this.selectByValue(this.value, true)){
39786                     this.select(0, true);
39787                 }
39788             }else{
39789                 this.selectNext();
39790                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39791                     this.taTask.delay(this.typeAheadDelay);
39792                 }
39793             }
39794         }else{
39795             this.onEmptyResults();
39796         }
39797         //this.el.focus();
39798     },
39799     // private
39800     onLoadException : function()
39801     {
39802         this.collapse();
39803         Roo.log(this.store.reader.jsonData);
39804         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39805             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39806         }
39807         
39808         
39809     },
39810     // private
39811     onTypeAhead : function(){
39812         if(this.store.getCount() > 0){
39813             var r = this.store.getAt(0);
39814             var newValue = r.data[this.displayField];
39815             var len = newValue.length;
39816             var selStart = this.getRawValue().length;
39817             if(selStart != len){
39818                 this.setRawValue(newValue);
39819                 this.selectText(selStart, newValue.length);
39820             }
39821         }
39822     },
39823
39824     // private
39825     onSelect : function(record, index){
39826         if(this.fireEvent('beforeselect', this, record, index) !== false){
39827             this.setFromData(index > -1 ? record.data : false);
39828             this.collapse();
39829             this.fireEvent('select', this, record, index);
39830         }
39831     },
39832
39833     /**
39834      * Returns the currently selected field value or empty string if no value is set.
39835      * @return {String} value The selected value
39836      */
39837     getValue : function(){
39838         if(this.valueField){
39839             return typeof this.value != 'undefined' ? this.value : '';
39840         }
39841         return Roo.form.ComboBox.superclass.getValue.call(this);
39842     },
39843
39844     /**
39845      * Clears any text/value currently set in the field
39846      */
39847     clearValue : function(){
39848         if(this.hiddenField){
39849             this.hiddenField.value = '';
39850         }
39851         this.value = '';
39852         this.setRawValue('');
39853         this.lastSelectionText = '';
39854         
39855     },
39856
39857     /**
39858      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39859      * will be displayed in the field.  If the value does not match the data value of an existing item,
39860      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39861      * Otherwise the field will be blank (although the value will still be set).
39862      * @param {String} value The value to match
39863      */
39864     setValue : function(v){
39865         var text = v;
39866         if(this.valueField){
39867             var r = this.findRecord(this.valueField, v);
39868             if(r){
39869                 text = r.data[this.displayField];
39870             }else if(this.valueNotFoundText !== undefined){
39871                 text = this.valueNotFoundText;
39872             }
39873         }
39874         this.lastSelectionText = text;
39875         if(this.hiddenField){
39876             this.hiddenField.value = v;
39877         }
39878         Roo.form.ComboBox.superclass.setValue.call(this, text);
39879         this.value = v;
39880     },
39881     /**
39882      * @property {Object} the last set data for the element
39883      */
39884     
39885     lastData : false,
39886     /**
39887      * Sets the value of the field based on a object which is related to the record format for the store.
39888      * @param {Object} value the value to set as. or false on reset?
39889      */
39890     setFromData : function(o){
39891         var dv = ''; // display value
39892         var vv = ''; // value value..
39893         this.lastData = o;
39894         if (this.displayField) {
39895             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39896         } else {
39897             // this is an error condition!!!
39898             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39899         }
39900         
39901         if(this.valueField){
39902             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39903         }
39904         if(this.hiddenField){
39905             this.hiddenField.value = vv;
39906             
39907             this.lastSelectionText = dv;
39908             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39909             this.value = vv;
39910             return;
39911         }
39912         // no hidden field.. - we store the value in 'value', but still display
39913         // display field!!!!
39914         this.lastSelectionText = dv;
39915         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39916         this.value = vv;
39917         
39918         
39919     },
39920     // private
39921     reset : function(){
39922         // overridden so that last data is reset..
39923         this.setValue(this.resetValue);
39924         this.clearInvalid();
39925         this.lastData = false;
39926         if (this.view) {
39927             this.view.clearSelections();
39928         }
39929     },
39930     // private
39931     findRecord : function(prop, value){
39932         var record;
39933         if(this.store.getCount() > 0){
39934             this.store.each(function(r){
39935                 if(r.data[prop] == value){
39936                     record = r;
39937                     return false;
39938                 }
39939                 return true;
39940             });
39941         }
39942         return record;
39943     },
39944     
39945     getName: function()
39946     {
39947         // returns hidden if it's set..
39948         if (!this.rendered) {return ''};
39949         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39950         
39951     },
39952     // private
39953     onViewMove : function(e, t){
39954         this.inKeyMode = false;
39955     },
39956
39957     // private
39958     onViewOver : function(e, t){
39959         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39960             return;
39961         }
39962         var item = this.view.findItemFromChild(t);
39963         if(item){
39964             var index = this.view.indexOf(item);
39965             this.select(index, false);
39966         }
39967     },
39968
39969     // private
39970     onViewClick : function(doFocus)
39971     {
39972         var index = this.view.getSelectedIndexes()[0];
39973         var r = this.store.getAt(index);
39974         if(r){
39975             this.onSelect(r, index);
39976         }
39977         if(doFocus !== false && !this.blockFocus){
39978             this.el.focus();
39979         }
39980     },
39981
39982     // private
39983     restrictHeight : function(){
39984         this.innerList.dom.style.height = '';
39985         var inner = this.innerList.dom;
39986         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39987         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39988         this.list.beginUpdate();
39989         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39990         this.list.alignTo(this.el, this.listAlign);
39991         this.list.endUpdate();
39992     },
39993
39994     // private
39995     onEmptyResults : function(){
39996         this.collapse();
39997     },
39998
39999     /**
40000      * Returns true if the dropdown list is expanded, else false.
40001      */
40002     isExpanded : function(){
40003         return this.list.isVisible();
40004     },
40005
40006     /**
40007      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40008      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40009      * @param {String} value The data value of the item to select
40010      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40011      * selected item if it is not currently in view (defaults to true)
40012      * @return {Boolean} True if the value matched an item in the list, else false
40013      */
40014     selectByValue : function(v, scrollIntoView){
40015         if(v !== undefined && v !== null){
40016             var r = this.findRecord(this.valueField || this.displayField, v);
40017             if(r){
40018                 this.select(this.store.indexOf(r), scrollIntoView);
40019                 return true;
40020             }
40021         }
40022         return false;
40023     },
40024
40025     /**
40026      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40027      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40028      * @param {Number} index The zero-based index of the list item to select
40029      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40030      * selected item if it is not currently in view (defaults to true)
40031      */
40032     select : function(index, scrollIntoView){
40033         this.selectedIndex = index;
40034         this.view.select(index);
40035         if(scrollIntoView !== false){
40036             var el = this.view.getNode(index);
40037             if(el){
40038                 this.innerList.scrollChildIntoView(el, false);
40039             }
40040         }
40041     },
40042
40043     // private
40044     selectNext : function(){
40045         var ct = this.store.getCount();
40046         if(ct > 0){
40047             if(this.selectedIndex == -1){
40048                 this.select(0);
40049             }else if(this.selectedIndex < ct-1){
40050                 this.select(this.selectedIndex+1);
40051             }
40052         }
40053     },
40054
40055     // private
40056     selectPrev : function(){
40057         var ct = this.store.getCount();
40058         if(ct > 0){
40059             if(this.selectedIndex == -1){
40060                 this.select(0);
40061             }else if(this.selectedIndex != 0){
40062                 this.select(this.selectedIndex-1);
40063             }
40064         }
40065     },
40066
40067     // private
40068     onKeyUp : function(e){
40069         if(this.editable !== false && !e.isSpecialKey()){
40070             this.lastKey = e.getKey();
40071             this.dqTask.delay(this.queryDelay);
40072         }
40073     },
40074
40075     // private
40076     validateBlur : function(){
40077         return !this.list || !this.list.isVisible();   
40078     },
40079
40080     // private
40081     initQuery : function(){
40082         this.doQuery(this.getRawValue());
40083     },
40084
40085     // private
40086     doForce : function(){
40087         if(this.el.dom.value.length > 0){
40088             this.el.dom.value =
40089                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40090              
40091         }
40092     },
40093
40094     /**
40095      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40096      * query allowing the query action to be canceled if needed.
40097      * @param {String} query The SQL query to execute
40098      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40099      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40100      * saved in the current store (defaults to false)
40101      */
40102     doQuery : function(q, forceAll){
40103         if(q === undefined || q === null){
40104             q = '';
40105         }
40106         var qe = {
40107             query: q,
40108             forceAll: forceAll,
40109             combo: this,
40110             cancel:false
40111         };
40112         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40113             return false;
40114         }
40115         q = qe.query;
40116         forceAll = qe.forceAll;
40117         if(forceAll === true || (q.length >= this.minChars)){
40118             if(this.lastQuery != q || this.alwaysQuery){
40119                 this.lastQuery = q;
40120                 if(this.mode == 'local'){
40121                     this.selectedIndex = -1;
40122                     if(forceAll){
40123                         this.store.clearFilter();
40124                     }else{
40125                         this.store.filter(this.displayField, q);
40126                     }
40127                     this.onLoad();
40128                 }else{
40129                     this.store.baseParams[this.queryParam] = q;
40130                     this.store.load({
40131                         params: this.getParams(q)
40132                     });
40133                     this.expand();
40134                 }
40135             }else{
40136                 this.selectedIndex = -1;
40137                 this.onLoad();   
40138             }
40139         }
40140     },
40141
40142     // private
40143     getParams : function(q){
40144         var p = {};
40145         //p[this.queryParam] = q;
40146         if(this.pageSize){
40147             p.start = 0;
40148             p.limit = this.pageSize;
40149         }
40150         return p;
40151     },
40152
40153     /**
40154      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40155      */
40156     collapse : function(){
40157         if(!this.isExpanded()){
40158             return;
40159         }
40160         this.list.hide();
40161         Roo.get(document).un('mousedown', this.collapseIf, this);
40162         Roo.get(document).un('mousewheel', this.collapseIf, this);
40163         if (!this.editable) {
40164             Roo.get(document).un('keydown', this.listKeyPress, this);
40165         }
40166         this.fireEvent('collapse', this);
40167     },
40168
40169     // private
40170     collapseIf : function(e){
40171         if(!e.within(this.wrap) && !e.within(this.list)){
40172             this.collapse();
40173         }
40174     },
40175
40176     /**
40177      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40178      */
40179     expand : function(){
40180         if(this.isExpanded() || !this.hasFocus){
40181             return;
40182         }
40183         this.list.alignTo(this.el, this.listAlign);
40184         this.list.show();
40185         Roo.get(document).on('mousedown', this.collapseIf, this);
40186         Roo.get(document).on('mousewheel', this.collapseIf, this);
40187         if (!this.editable) {
40188             Roo.get(document).on('keydown', this.listKeyPress, this);
40189         }
40190         
40191         this.fireEvent('expand', this);
40192     },
40193
40194     // private
40195     // Implements the default empty TriggerField.onTriggerClick function
40196     onTriggerClick : function(){
40197         if(this.disabled){
40198             return;
40199         }
40200         if(this.isExpanded()){
40201             this.collapse();
40202             if (!this.blockFocus) {
40203                 this.el.focus();
40204             }
40205             
40206         }else {
40207             this.hasFocus = true;
40208             if(this.triggerAction == 'all') {
40209                 this.doQuery(this.allQuery, true);
40210             } else {
40211                 this.doQuery(this.getRawValue());
40212             }
40213             if (!this.blockFocus) {
40214                 this.el.focus();
40215             }
40216         }
40217     },
40218     listKeyPress : function(e)
40219     {
40220         //Roo.log('listkeypress');
40221         // scroll to first matching element based on key pres..
40222         if (e.isSpecialKey()) {
40223             return false;
40224         }
40225         var k = String.fromCharCode(e.getKey()).toUpperCase();
40226         //Roo.log(k);
40227         var match  = false;
40228         var csel = this.view.getSelectedNodes();
40229         var cselitem = false;
40230         if (csel.length) {
40231             var ix = this.view.indexOf(csel[0]);
40232             cselitem  = this.store.getAt(ix);
40233             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40234                 cselitem = false;
40235             }
40236             
40237         }
40238         
40239         this.store.each(function(v) { 
40240             if (cselitem) {
40241                 // start at existing selection.
40242                 if (cselitem.id == v.id) {
40243                     cselitem = false;
40244                 }
40245                 return;
40246             }
40247                 
40248             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40249                 match = this.store.indexOf(v);
40250                 return false;
40251             }
40252         }, this);
40253         
40254         if (match === false) {
40255             return true; // no more action?
40256         }
40257         // scroll to?
40258         this.view.select(match);
40259         var sn = Roo.get(this.view.getSelectedNodes()[0])
40260         sn.scrollIntoView(sn.dom.parentNode, false);
40261     }
40262
40263     /** 
40264     * @cfg {Boolean} grow 
40265     * @hide 
40266     */
40267     /** 
40268     * @cfg {Number} growMin 
40269     * @hide 
40270     */
40271     /** 
40272     * @cfg {Number} growMax 
40273     * @hide 
40274     */
40275     /**
40276      * @hide
40277      * @method autoSize
40278      */
40279 });/*
40280  * Copyright(c) 2010-2012, Roo J Solutions Limited
40281  *
40282  * Licence LGPL
40283  *
40284  */
40285
40286 /**
40287  * @class Roo.form.ComboBoxArray
40288  * @extends Roo.form.TextField
40289  * A facebook style adder... for lists of email / people / countries  etc...
40290  * pick multiple items from a combo box, and shows each one.
40291  *
40292  *  Fred [x]  Brian [x]  [Pick another |v]
40293  *
40294  *
40295  *  For this to work: it needs various extra information
40296  *    - normal combo problay has
40297  *      name, hiddenName
40298  *    + displayField, valueField
40299  *
40300  *    For our purpose...
40301  *
40302  *
40303  *   If we change from 'extends' to wrapping...
40304  *   
40305  *  
40306  *
40307  
40308  
40309  * @constructor
40310  * Create a new ComboBoxArray.
40311  * @param {Object} config Configuration options
40312  */
40313  
40314
40315 Roo.form.ComboBoxArray = function(config)
40316 {
40317     this.addEvents({
40318         /**
40319          * @event beforeremove
40320          * Fires before remove the value from the list
40321              * @param {Roo.form.ComboBoxArray} _self This combo box array
40322              * @param {Roo.form.ComboBoxArray.Item} item removed item
40323              */
40324         'beforeremove' : true,
40325         /**
40326          * @event remove
40327          * Fires when remove the value from the list
40328              * @param {Roo.form.ComboBoxArray} _self This combo box array
40329              * @param {Roo.form.ComboBoxArray.Item} item removed item
40330              */
40331         'remove' : true
40332         
40333         
40334     });
40335     
40336     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40337     
40338     this.items = new Roo.util.MixedCollection(false);
40339     
40340     // construct the child combo...
40341     
40342     
40343     
40344     
40345    
40346     
40347 }
40348
40349  
40350 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40351
40352     /**
40353      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40354      */
40355     
40356     lastData : false,
40357     
40358     // behavies liek a hiddne field
40359     inputType:      'hidden',
40360     /**
40361      * @cfg {Number} width The width of the box that displays the selected element
40362      */ 
40363     width:          300,
40364
40365     
40366     
40367     /**
40368      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40369      */
40370     name : false,
40371     /**
40372      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40373      */
40374     hiddenName : false,
40375     
40376     
40377     // private the array of items that are displayed..
40378     items  : false,
40379     // private - the hidden field el.
40380     hiddenEl : false,
40381     // private - the filed el..
40382     el : false,
40383     
40384     //validateValue : function() { return true; }, // all values are ok!
40385     //onAddClick: function() { },
40386     
40387     onRender : function(ct, position) 
40388     {
40389         
40390         // create the standard hidden element
40391         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40392         
40393         
40394         // give fake names to child combo;
40395         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40396         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40397         
40398         this.combo = Roo.factory(this.combo, Roo.form);
40399         this.combo.onRender(ct, position);
40400         if (typeof(this.combo.width) != 'undefined') {
40401             this.combo.onResize(this.combo.width,0);
40402         }
40403         
40404         this.combo.initEvents();
40405         
40406         // assigned so form know we need to do this..
40407         this.store          = this.combo.store;
40408         this.valueField     = this.combo.valueField;
40409         this.displayField   = this.combo.displayField ;
40410         
40411         
40412         this.combo.wrap.addClass('x-cbarray-grp');
40413         
40414         var cbwrap = this.combo.wrap.createChild(
40415             {tag: 'div', cls: 'x-cbarray-cb'},
40416             this.combo.el.dom
40417         );
40418         
40419              
40420         this.hiddenEl = this.combo.wrap.createChild({
40421             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40422         });
40423         this.el = this.combo.wrap.createChild({
40424             tag: 'input',  type:'hidden' , name: this.name, value : ''
40425         });
40426          //   this.el.dom.removeAttribute("name");
40427         
40428         
40429         this.outerWrap = this.combo.wrap;
40430         this.wrap = cbwrap;
40431         
40432         this.outerWrap.setWidth(this.width);
40433         this.outerWrap.dom.removeChild(this.el.dom);
40434         
40435         this.wrap.dom.appendChild(this.el.dom);
40436         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40437         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40438         
40439         this.combo.trigger.setStyle('position','relative');
40440         this.combo.trigger.setStyle('left', '0px');
40441         this.combo.trigger.setStyle('top', '2px');
40442         
40443         this.combo.el.setStyle('vertical-align', 'text-bottom');
40444         
40445         //this.trigger.setStyle('vertical-align', 'top');
40446         
40447         // this should use the code from combo really... on('add' ....)
40448         if (this.adder) {
40449             
40450         
40451             this.adder = this.outerWrap.createChild(
40452                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40453             var _t = this;
40454             this.adder.on('click', function(e) {
40455                 _t.fireEvent('adderclick', this, e);
40456             }, _t);
40457         }
40458         //var _t = this;
40459         //this.adder.on('click', this.onAddClick, _t);
40460         
40461         
40462         this.combo.on('select', function(cb, rec, ix) {
40463             this.addItem(rec.data);
40464             
40465             cb.setValue('');
40466             cb.el.dom.value = '';
40467             //cb.lastData = rec.data;
40468             // add to list
40469             
40470         }, this);
40471         
40472         
40473     },
40474     
40475     
40476     getName: function()
40477     {
40478         // returns hidden if it's set..
40479         if (!this.rendered) {return ''};
40480         return  this.hiddenName ? this.hiddenName : this.name;
40481         
40482     },
40483     
40484     
40485     onResize: function(w, h){
40486         
40487         return;
40488         // not sure if this is needed..
40489         //this.combo.onResize(w,h);
40490         
40491         if(typeof w != 'number'){
40492             // we do not handle it!?!?
40493             return;
40494         }
40495         var tw = this.combo.trigger.getWidth();
40496         tw += this.addicon ? this.addicon.getWidth() : 0;
40497         tw += this.editicon ? this.editicon.getWidth() : 0;
40498         var x = w - tw;
40499         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40500             
40501         this.combo.trigger.setStyle('left', '0px');
40502         
40503         if(this.list && this.listWidth === undefined){
40504             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40505             this.list.setWidth(lw);
40506             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40507         }
40508         
40509     
40510         
40511     },
40512     
40513     addItem: function(rec)
40514     {
40515         var valueField = this.combo.valueField;
40516         var displayField = this.combo.displayField;
40517         if (this.items.indexOfKey(rec[valueField]) > -1) {
40518             //console.log("GOT " + rec.data.id);
40519             return;
40520         }
40521         
40522         var x = new Roo.form.ComboBoxArray.Item({
40523             //id : rec[this.idField],
40524             data : rec,
40525             displayField : displayField ,
40526             tipField : displayField ,
40527             cb : this
40528         });
40529         // use the 
40530         this.items.add(rec[valueField],x);
40531         // add it before the element..
40532         this.updateHiddenEl();
40533         x.render(this.outerWrap, this.wrap.dom);
40534         // add the image handler..
40535     },
40536     
40537     updateHiddenEl : function()
40538     {
40539         this.validate();
40540         if (!this.hiddenEl) {
40541             return;
40542         }
40543         var ar = [];
40544         var idField = this.combo.valueField;
40545         
40546         this.items.each(function(f) {
40547             ar.push(f.data[idField]);
40548            
40549         });
40550         this.hiddenEl.dom.value = ar.join(',');
40551         this.validate();
40552     },
40553     
40554     reset : function()
40555     {
40556         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40557         this.items.each(function(f) {
40558            f.remove(); 
40559         });
40560         this.el.dom.value = '';
40561         if (this.hiddenEl) {
40562             this.hiddenEl.dom.value = '';
40563         }
40564         
40565     },
40566     getValue: function()
40567     {
40568         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40569     },
40570     setValue: function(v) // not a valid action - must use addItems..
40571     {
40572          
40573         this.reset();
40574         
40575         
40576         
40577         if (this.store.isLocal && (typeof(v) == 'string')) {
40578             // then we can use the store to find the values..
40579             // comma seperated at present.. this needs to allow JSON based encoding..
40580             this.hiddenEl.value  = v;
40581             var v_ar = [];
40582             Roo.each(v.split(','), function(k) {
40583                 Roo.log("CHECK " + this.valueField + ',' + k);
40584                 var li = this.store.query(this.valueField, k);
40585                 if (!li.length) {
40586                     return;
40587                 }
40588                 var add = {};
40589                 add[this.valueField] = k;
40590                 add[this.displayField] = li.item(0).data[this.displayField];
40591                 
40592                 this.addItem(add);
40593             }, this) 
40594              
40595         }
40596         if (typeof(v) == 'object' ) {
40597             // then let's assume it's an array of objects..
40598             Roo.each(v, function(l) {
40599                 this.addItem(l);
40600             }, this);
40601              
40602         }
40603         
40604         
40605     },
40606     setFromData: function(v)
40607     {
40608         // this recieves an object, if setValues is called.
40609         this.reset();
40610         this.el.dom.value = v[this.displayField];
40611         this.hiddenEl.dom.value = v[this.valueField];
40612         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40613             return;
40614         }
40615         var kv = v[this.valueField];
40616         var dv = v[this.displayField];
40617         kv = typeof(kv) != 'string' ? '' : kv;
40618         dv = typeof(dv) != 'string' ? '' : dv;
40619         
40620         
40621         var keys = kv.split(',');
40622         var display = dv.split(',');
40623         for (var i = 0 ; i < keys.length; i++) {
40624             
40625             add = {};
40626             add[this.valueField] = keys[i];
40627             add[this.displayField] = display[i];
40628             this.addItem(add);
40629         }
40630       
40631         
40632     },
40633     
40634     /**
40635      * Validates the combox array value
40636      * @return {Boolean} True if the value is valid, else false
40637      */
40638     validate : function(){
40639         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40640             this.clearInvalid();
40641             return true;
40642         }
40643         return false;
40644     },
40645     
40646     validateValue : function(value){
40647         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40648         
40649     },
40650     
40651     /*@
40652      * overide
40653      * 
40654      */
40655     isDirty : function() {
40656         if(this.disabled) {
40657             return false;
40658         }
40659         
40660         try {
40661             var d = Roo.decode(String(this.originalValue));
40662         } catch (e) {
40663             return String(this.getValue()) !== String(this.originalValue);
40664         }
40665         
40666         var originalValue = [];
40667         
40668         for (var i = 0; i < d.length; i++){
40669             originalValue.push(d[i][this.valueField]);
40670         }
40671         
40672         return String(this.getValue()) !== String(originalValue.join(','));
40673         
40674     }
40675     
40676 });
40677
40678
40679
40680 /**
40681  * @class Roo.form.ComboBoxArray.Item
40682  * @extends Roo.BoxComponent
40683  * A selected item in the list
40684  *  Fred [x]  Brian [x]  [Pick another |v]
40685  * 
40686  * @constructor
40687  * Create a new item.
40688  * @param {Object} config Configuration options
40689  */
40690  
40691 Roo.form.ComboBoxArray.Item = function(config) {
40692     config.id = Roo.id();
40693     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40694 }
40695
40696 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40697     data : {},
40698     cb: false,
40699     displayField : false,
40700     tipField : false,
40701     
40702     
40703     defaultAutoCreate : {
40704         tag: 'div',
40705         cls: 'x-cbarray-item',
40706         cn : [ 
40707             { tag: 'div' },
40708             {
40709                 tag: 'img',
40710                 width:16,
40711                 height : 16,
40712                 src : Roo.BLANK_IMAGE_URL ,
40713                 align: 'center'
40714             }
40715         ]
40716         
40717     },
40718     
40719  
40720     onRender : function(ct, position)
40721     {
40722         Roo.form.Field.superclass.onRender.call(this, ct, position);
40723         
40724         if(!this.el){
40725             var cfg = this.getAutoCreate();
40726             this.el = ct.createChild(cfg, position);
40727         }
40728         
40729         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40730         
40731         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40732             this.cb.renderer(this.data) :
40733             String.format('{0}',this.data[this.displayField]);
40734         
40735             
40736         this.el.child('div').dom.setAttribute('qtip',
40737                         String.format('{0}',this.data[this.tipField])
40738         );
40739         
40740         this.el.child('img').on('click', this.remove, this);
40741         
40742     },
40743    
40744     remove : function()
40745     {
40746         if(this.cb.disabled){
40747             return;
40748         }
40749         
40750         if(false !== this.cb.fireEvent('remove', this.cb, this)){
40751             this.cb.items.remove(this);
40752             this.el.child('img').un('click', this.remove, this);
40753             this.el.remove();
40754             this.cb.updateHiddenEl();
40755
40756             this.cb.fireEvent('remove', this.cb, this);
40757         }
40758         
40759     }
40760 });/*
40761  * Based on:
40762  * Ext JS Library 1.1.1
40763  * Copyright(c) 2006-2007, Ext JS, LLC.
40764  *
40765  * Originally Released Under LGPL - original licence link has changed is not relivant.
40766  *
40767  * Fork - LGPL
40768  * <script type="text/javascript">
40769  */
40770 /**
40771  * @class Roo.form.Checkbox
40772  * @extends Roo.form.Field
40773  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40774  * @constructor
40775  * Creates a new Checkbox
40776  * @param {Object} config Configuration options
40777  */
40778 Roo.form.Checkbox = function(config){
40779     Roo.form.Checkbox.superclass.constructor.call(this, config);
40780     this.addEvents({
40781         /**
40782          * @event check
40783          * Fires when the checkbox is checked or unchecked.
40784              * @param {Roo.form.Checkbox} this This checkbox
40785              * @param {Boolean} checked The new checked value
40786              */
40787         check : true
40788     });
40789 };
40790
40791 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40792     /**
40793      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40794      */
40795     focusClass : undefined,
40796     /**
40797      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40798      */
40799     fieldClass: "x-form-field",
40800     /**
40801      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40802      */
40803     checked: false,
40804     /**
40805      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40806      * {tag: "input", type: "checkbox", autocomplete: "off"})
40807      */
40808     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40809     /**
40810      * @cfg {String} boxLabel The text that appears beside the checkbox
40811      */
40812     boxLabel : "",
40813     /**
40814      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40815      */  
40816     inputValue : '1',
40817     /**
40818      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40819      */
40820      valueOff: '0', // value when not checked..
40821
40822     actionMode : 'viewEl', 
40823     //
40824     // private
40825     itemCls : 'x-menu-check-item x-form-item',
40826     groupClass : 'x-menu-group-item',
40827     inputType : 'hidden',
40828     
40829     
40830     inSetChecked: false, // check that we are not calling self...
40831     
40832     inputElement: false, // real input element?
40833     basedOn: false, // ????
40834     
40835     isFormField: true, // not sure where this is needed!!!!
40836
40837     onResize : function(){
40838         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40839         if(!this.boxLabel){
40840             this.el.alignTo(this.wrap, 'c-c');
40841         }
40842     },
40843
40844     initEvents : function(){
40845         Roo.form.Checkbox.superclass.initEvents.call(this);
40846         this.el.on("click", this.onClick,  this);
40847         this.el.on("change", this.onClick,  this);
40848     },
40849
40850
40851     getResizeEl : function(){
40852         return this.wrap;
40853     },
40854
40855     getPositionEl : function(){
40856         return this.wrap;
40857     },
40858
40859     // private
40860     onRender : function(ct, position){
40861         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40862         /*
40863         if(this.inputValue !== undefined){
40864             this.el.dom.value = this.inputValue;
40865         }
40866         */
40867         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40868         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40869         var viewEl = this.wrap.createChild({ 
40870             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40871         this.viewEl = viewEl;   
40872         this.wrap.on('click', this.onClick,  this); 
40873         
40874         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40875         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40876         
40877         
40878         
40879         if(this.boxLabel){
40880             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40881         //    viewEl.on('click', this.onClick,  this); 
40882         }
40883         //if(this.checked){
40884             this.setChecked(this.checked);
40885         //}else{
40886             //this.checked = this.el.dom;
40887         //}
40888
40889     },
40890
40891     // private
40892     initValue : Roo.emptyFn,
40893
40894     /**
40895      * Returns the checked state of the checkbox.
40896      * @return {Boolean} True if checked, else false
40897      */
40898     getValue : function(){
40899         if(this.el){
40900             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40901         }
40902         return this.valueOff;
40903         
40904     },
40905
40906         // private
40907     onClick : function(){ 
40908         if (this.disabled) {
40909             return;
40910         }
40911         this.setChecked(!this.checked);
40912
40913         //if(this.el.dom.checked != this.checked){
40914         //    this.setValue(this.el.dom.checked);
40915        // }
40916     },
40917
40918     /**
40919      * Sets the checked state of the checkbox.
40920      * On is always based on a string comparison between inputValue and the param.
40921      * @param {Boolean/String} value - the value to set 
40922      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40923      */
40924     setValue : function(v,suppressEvent){
40925         
40926         
40927         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40928         //if(this.el && this.el.dom){
40929         //    this.el.dom.checked = this.checked;
40930         //    this.el.dom.defaultChecked = this.checked;
40931         //}
40932         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40933         //this.fireEvent("check", this, this.checked);
40934     },
40935     // private..
40936     setChecked : function(state,suppressEvent)
40937     {
40938         if (this.inSetChecked) {
40939             this.checked = state;
40940             return;
40941         }
40942         
40943     
40944         if(this.wrap){
40945             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40946         }
40947         this.checked = state;
40948         if(suppressEvent !== true){
40949             this.fireEvent('check', this, state);
40950         }
40951         this.inSetChecked = true;
40952         this.el.dom.value = state ? this.inputValue : this.valueOff;
40953         this.inSetChecked = false;
40954         
40955     },
40956     // handle setting of hidden value by some other method!!?!?
40957     setFromHidden: function()
40958     {
40959         if(!this.el){
40960             return;
40961         }
40962         //console.log("SET FROM HIDDEN");
40963         //alert('setFrom hidden');
40964         this.setValue(this.el.dom.value);
40965     },
40966     
40967     onDestroy : function()
40968     {
40969         if(this.viewEl){
40970             Roo.get(this.viewEl).remove();
40971         }
40972          
40973         Roo.form.Checkbox.superclass.onDestroy.call(this);
40974     }
40975
40976 });/*
40977  * Based on:
40978  * Ext JS Library 1.1.1
40979  * Copyright(c) 2006-2007, Ext JS, LLC.
40980  *
40981  * Originally Released Under LGPL - original licence link has changed is not relivant.
40982  *
40983  * Fork - LGPL
40984  * <script type="text/javascript">
40985  */
40986  
40987 /**
40988  * @class Roo.form.Radio
40989  * @extends Roo.form.Checkbox
40990  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40991  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40992  * @constructor
40993  * Creates a new Radio
40994  * @param {Object} config Configuration options
40995  */
40996 Roo.form.Radio = function(){
40997     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40998 };
40999 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41000     inputType: 'radio',
41001
41002     /**
41003      * If this radio is part of a group, it will return the selected value
41004      * @return {String}
41005      */
41006     getGroupValue : function(){
41007         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41008     },
41009     
41010     
41011     onRender : function(ct, position){
41012         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41013         
41014         if(this.inputValue !== undefined){
41015             this.el.dom.value = this.inputValue;
41016         }
41017          
41018         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41019         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41020         //var viewEl = this.wrap.createChild({ 
41021         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41022         //this.viewEl = viewEl;   
41023         //this.wrap.on('click', this.onClick,  this); 
41024         
41025         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41026         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41027         
41028         
41029         
41030         if(this.boxLabel){
41031             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41032         //    viewEl.on('click', this.onClick,  this); 
41033         }
41034          if(this.checked){
41035             this.el.dom.checked =   'checked' ;
41036         }
41037          
41038     } 
41039     
41040     
41041 });//<script type="text/javascript">
41042
41043 /*
41044  * Based  Ext JS Library 1.1.1
41045  * Copyright(c) 2006-2007, Ext JS, LLC.
41046  * LGPL
41047  *
41048  */
41049  
41050 /**
41051  * @class Roo.HtmlEditorCore
41052  * @extends Roo.Component
41053  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41054  *
41055  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41056  */
41057
41058 Roo.HtmlEditorCore = function(config){
41059     
41060     
41061     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41062     
41063     
41064     this.addEvents({
41065         /**
41066          * @event initialize
41067          * Fires when the editor is fully initialized (including the iframe)
41068          * @param {Roo.HtmlEditorCore} this
41069          */
41070         initialize: true,
41071         /**
41072          * @event activate
41073          * Fires when the editor is first receives the focus. Any insertion must wait
41074          * until after this event.
41075          * @param {Roo.HtmlEditorCore} this
41076          */
41077         activate: true,
41078          /**
41079          * @event beforesync
41080          * Fires before the textarea is updated with content from the editor iframe. Return false
41081          * to cancel the sync.
41082          * @param {Roo.HtmlEditorCore} this
41083          * @param {String} html
41084          */
41085         beforesync: true,
41086          /**
41087          * @event beforepush
41088          * Fires before the iframe editor is updated with content from the textarea. Return false
41089          * to cancel the push.
41090          * @param {Roo.HtmlEditorCore} this
41091          * @param {String} html
41092          */
41093         beforepush: true,
41094          /**
41095          * @event sync
41096          * Fires when the textarea is updated with content from the editor iframe.
41097          * @param {Roo.HtmlEditorCore} this
41098          * @param {String} html
41099          */
41100         sync: true,
41101          /**
41102          * @event push
41103          * Fires when the iframe editor is updated with content from the textarea.
41104          * @param {Roo.HtmlEditorCore} this
41105          * @param {String} html
41106          */
41107         push: true,
41108         
41109         /**
41110          * @event editorevent
41111          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41112          * @param {Roo.HtmlEditorCore} this
41113          */
41114         editorevent: true
41115     });
41116     
41117     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41118     
41119     // defaults : white / black...
41120     this.applyBlacklists();
41121     
41122     
41123     
41124 };
41125
41126
41127 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41128
41129
41130      /**
41131      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41132      */
41133     
41134     owner : false,
41135     
41136      /**
41137      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41138      *                        Roo.resizable.
41139      */
41140     resizable : false,
41141      /**
41142      * @cfg {Number} height (in pixels)
41143      */   
41144     height: 300,
41145    /**
41146      * @cfg {Number} width (in pixels)
41147      */   
41148     width: 500,
41149     
41150     /**
41151      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41152      * 
41153      */
41154     stylesheets: false,
41155     
41156     // id of frame..
41157     frameId: false,
41158     
41159     // private properties
41160     validationEvent : false,
41161     deferHeight: true,
41162     initialized : false,
41163     activated : false,
41164     sourceEditMode : false,
41165     onFocus : Roo.emptyFn,
41166     iframePad:3,
41167     hideMode:'offsets',
41168     
41169     clearUp: true,
41170     
41171     // blacklist + whitelisted elements..
41172     black: false,
41173     white: false,
41174      
41175     
41176
41177     /**
41178      * Protected method that will not generally be called directly. It
41179      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41180      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41181      */
41182     getDocMarkup : function(){
41183         // body styles..
41184         var st = '';
41185         Roo.log(this.stylesheets);
41186         
41187         // inherit styels from page...?? 
41188         if (this.stylesheets === false) {
41189             
41190             Roo.get(document.head).select('style').each(function(node) {
41191                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41192             });
41193             
41194             Roo.get(document.head).select('link').each(function(node) { 
41195                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41196             });
41197             
41198         } else if (!this.stylesheets.length) {
41199                 // simple..
41200                 st = '<style type="text/css">' +
41201                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41202                    '</style>';
41203         } else {
41204             Roo.each(this.stylesheets, function(s) {
41205                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41206             });
41207             
41208         }
41209         
41210         st +=  '<style type="text/css">' +
41211             'IMG { cursor: pointer } ' +
41212         '</style>';
41213
41214         
41215         return '<html><head>' + st  +
41216             //<style type="text/css">' +
41217             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41218             //'</style>' +
41219             ' </head><body class="roo-htmleditor-body"></body></html>';
41220     },
41221
41222     // private
41223     onRender : function(ct, position)
41224     {
41225         var _t = this;
41226         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41227         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41228         
41229         
41230         this.el.dom.style.border = '0 none';
41231         this.el.dom.setAttribute('tabIndex', -1);
41232         this.el.addClass('x-hidden hide');
41233         
41234         
41235         
41236         if(Roo.isIE){ // fix IE 1px bogus margin
41237             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41238         }
41239        
41240         
41241         this.frameId = Roo.id();
41242         
41243          
41244         
41245         var iframe = this.owner.wrap.createChild({
41246             tag: 'iframe',
41247             cls: 'form-control', // bootstrap..
41248             id: this.frameId,
41249             name: this.frameId,
41250             frameBorder : 'no',
41251             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41252         }, this.el
41253         );
41254         
41255         
41256         this.iframe = iframe.dom;
41257
41258          this.assignDocWin();
41259         
41260         this.doc.designMode = 'on';
41261        
41262         this.doc.open();
41263         this.doc.write(this.getDocMarkup());
41264         this.doc.close();
41265
41266         
41267         var task = { // must defer to wait for browser to be ready
41268             run : function(){
41269                 //console.log("run task?" + this.doc.readyState);
41270                 this.assignDocWin();
41271                 if(this.doc.body || this.doc.readyState == 'complete'){
41272                     try {
41273                         this.doc.designMode="on";
41274                     } catch (e) {
41275                         return;
41276                     }
41277                     Roo.TaskMgr.stop(task);
41278                     this.initEditor.defer(10, this);
41279                 }
41280             },
41281             interval : 10,
41282             duration: 10000,
41283             scope: this
41284         };
41285         Roo.TaskMgr.start(task);
41286
41287         
41288          
41289     },
41290
41291     // private
41292     onResize : function(w, h)
41293     {
41294          Roo.log('resize: ' +w + ',' + h );
41295         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41296         if(!this.iframe){
41297             return;
41298         }
41299         if(typeof w == 'number'){
41300             
41301             this.iframe.style.width = w + 'px';
41302         }
41303         if(typeof h == 'number'){
41304             
41305             this.iframe.style.height = h + 'px';
41306             if(this.doc){
41307                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41308             }
41309         }
41310         
41311     },
41312
41313     /**
41314      * Toggles the editor between standard and source edit mode.
41315      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41316      */
41317     toggleSourceEdit : function(sourceEditMode){
41318         
41319         this.sourceEditMode = sourceEditMode === true;
41320         
41321         if(this.sourceEditMode){
41322  
41323             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41324             
41325         }else{
41326             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41327             //this.iframe.className = '';
41328             this.deferFocus();
41329         }
41330         //this.setSize(this.owner.wrap.getSize());
41331         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41332     },
41333
41334     
41335   
41336
41337     /**
41338      * Protected method that will not generally be called directly. If you need/want
41339      * custom HTML cleanup, this is the method you should override.
41340      * @param {String} html The HTML to be cleaned
41341      * return {String} The cleaned HTML
41342      */
41343     cleanHtml : function(html){
41344         html = String(html);
41345         if(html.length > 5){
41346             if(Roo.isSafari){ // strip safari nonsense
41347                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41348             }
41349         }
41350         if(html == '&nbsp;'){
41351             html = '';
41352         }
41353         return html;
41354     },
41355
41356     /**
41357      * HTML Editor -> Textarea
41358      * Protected method that will not generally be called directly. Syncs the contents
41359      * of the editor iframe with the textarea.
41360      */
41361     syncValue : function(){
41362         if(this.initialized){
41363             var bd = (this.doc.body || this.doc.documentElement);
41364             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41365             var html = bd.innerHTML;
41366             if(Roo.isSafari){
41367                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41368                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41369                 if(m && m[1]){
41370                     html = '<div style="'+m[0]+'">' + html + '</div>';
41371                 }
41372             }
41373             html = this.cleanHtml(html);
41374             // fix up the special chars.. normaly like back quotes in word...
41375             // however we do not want to do this with chinese..
41376             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41377                 var cc = b.charCodeAt();
41378                 if (
41379                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41380                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41381                     (cc >= 0xf900 && cc < 0xfb00 )
41382                 ) {
41383                         return b;
41384                 }
41385                 return "&#"+cc+";" 
41386             });
41387             if(this.owner.fireEvent('beforesync', this, html) !== false){
41388                 this.el.dom.value = html;
41389                 this.owner.fireEvent('sync', this, html);
41390             }
41391         }
41392     },
41393
41394     /**
41395      * Protected method that will not generally be called directly. Pushes the value of the textarea
41396      * into the iframe editor.
41397      */
41398     pushValue : function(){
41399         if(this.initialized){
41400             var v = this.el.dom.value.trim();
41401             
41402 //            if(v.length < 1){
41403 //                v = '&#160;';
41404 //            }
41405             
41406             if(this.owner.fireEvent('beforepush', this, v) !== false){
41407                 var d = (this.doc.body || this.doc.documentElement);
41408                 d.innerHTML = v;
41409                 this.cleanUpPaste();
41410                 this.el.dom.value = d.innerHTML;
41411                 this.owner.fireEvent('push', this, v);
41412             }
41413         }
41414     },
41415
41416     // private
41417     deferFocus : function(){
41418         this.focus.defer(10, this);
41419     },
41420
41421     // doc'ed in Field
41422     focus : function(){
41423         if(this.win && !this.sourceEditMode){
41424             this.win.focus();
41425         }else{
41426             this.el.focus();
41427         }
41428     },
41429     
41430     assignDocWin: function()
41431     {
41432         var iframe = this.iframe;
41433         
41434          if(Roo.isIE){
41435             this.doc = iframe.contentWindow.document;
41436             this.win = iframe.contentWindow;
41437         } else {
41438 //            if (!Roo.get(this.frameId)) {
41439 //                return;
41440 //            }
41441 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41442 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41443             
41444             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41445                 return;
41446             }
41447             
41448             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41449             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41450         }
41451     },
41452     
41453     // private
41454     initEditor : function(){
41455         //console.log("INIT EDITOR");
41456         this.assignDocWin();
41457         
41458         
41459         
41460         this.doc.designMode="on";
41461         this.doc.open();
41462         this.doc.write(this.getDocMarkup());
41463         this.doc.close();
41464         
41465         var dbody = (this.doc.body || this.doc.documentElement);
41466         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41467         // this copies styles from the containing element into thsi one..
41468         // not sure why we need all of this..
41469         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41470         
41471         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41472         //ss['background-attachment'] = 'fixed'; // w3c
41473         dbody.bgProperties = 'fixed'; // ie
41474         //Roo.DomHelper.applyStyles(dbody, ss);
41475         Roo.EventManager.on(this.doc, {
41476             //'mousedown': this.onEditorEvent,
41477             'mouseup': this.onEditorEvent,
41478             'dblclick': this.onEditorEvent,
41479             'click': this.onEditorEvent,
41480             'keyup': this.onEditorEvent,
41481             buffer:100,
41482             scope: this
41483         });
41484         if(Roo.isGecko){
41485             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41486         }
41487         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41488             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41489         }
41490         this.initialized = true;
41491
41492         this.owner.fireEvent('initialize', this);
41493         this.pushValue();
41494     },
41495
41496     // private
41497     onDestroy : function(){
41498         
41499         
41500         
41501         if(this.rendered){
41502             
41503             //for (var i =0; i < this.toolbars.length;i++) {
41504             //    // fixme - ask toolbars for heights?
41505             //    this.toolbars[i].onDestroy();
41506            // }
41507             
41508             //this.wrap.dom.innerHTML = '';
41509             //this.wrap.remove();
41510         }
41511     },
41512
41513     // private
41514     onFirstFocus : function(){
41515         
41516         this.assignDocWin();
41517         
41518         
41519         this.activated = true;
41520          
41521     
41522         if(Roo.isGecko){ // prevent silly gecko errors
41523             this.win.focus();
41524             var s = this.win.getSelection();
41525             if(!s.focusNode || s.focusNode.nodeType != 3){
41526                 var r = s.getRangeAt(0);
41527                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41528                 r.collapse(true);
41529                 this.deferFocus();
41530             }
41531             try{
41532                 this.execCmd('useCSS', true);
41533                 this.execCmd('styleWithCSS', false);
41534             }catch(e){}
41535         }
41536         this.owner.fireEvent('activate', this);
41537     },
41538
41539     // private
41540     adjustFont: function(btn){
41541         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41542         //if(Roo.isSafari){ // safari
41543         //    adjust *= 2;
41544        // }
41545         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41546         if(Roo.isSafari){ // safari
41547             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41548             v =  (v < 10) ? 10 : v;
41549             v =  (v > 48) ? 48 : v;
41550             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41551             
41552         }
41553         
41554         
41555         v = Math.max(1, v+adjust);
41556         
41557         this.execCmd('FontSize', v  );
41558     },
41559
41560     onEditorEvent : function(e){
41561         this.owner.fireEvent('editorevent', this, e);
41562       //  this.updateToolbar();
41563         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41564     },
41565
41566     insertTag : function(tg)
41567     {
41568         // could be a bit smarter... -> wrap the current selected tRoo..
41569         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41570             
41571             range = this.createRange(this.getSelection());
41572             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41573             wrappingNode.appendChild(range.extractContents());
41574             range.insertNode(wrappingNode);
41575
41576             return;
41577             
41578             
41579             
41580         }
41581         this.execCmd("formatblock",   tg);
41582         
41583     },
41584     
41585     insertText : function(txt)
41586     {
41587         
41588         
41589         var range = this.createRange();
41590         range.deleteContents();
41591                //alert(Sender.getAttribute('label'));
41592                
41593         range.insertNode(this.doc.createTextNode(txt));
41594     } ,
41595     
41596      
41597
41598     /**
41599      * Executes a Midas editor command on the editor document and performs necessary focus and
41600      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41601      * @param {String} cmd The Midas command
41602      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41603      */
41604     relayCmd : function(cmd, value){
41605         this.win.focus();
41606         this.execCmd(cmd, value);
41607         this.owner.fireEvent('editorevent', this);
41608         //this.updateToolbar();
41609         this.owner.deferFocus();
41610     },
41611
41612     /**
41613      * Executes a Midas editor command directly on the editor document.
41614      * For visual commands, you should use {@link #relayCmd} instead.
41615      * <b>This should only be called after the editor is initialized.</b>
41616      * @param {String} cmd The Midas command
41617      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41618      */
41619     execCmd : function(cmd, value){
41620         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41621         this.syncValue();
41622     },
41623  
41624  
41625    
41626     /**
41627      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41628      * to insert tRoo.
41629      * @param {String} text | dom node.. 
41630      */
41631     insertAtCursor : function(text)
41632     {
41633         
41634         
41635         
41636         if(!this.activated){
41637             return;
41638         }
41639         /*
41640         if(Roo.isIE){
41641             this.win.focus();
41642             var r = this.doc.selection.createRange();
41643             if(r){
41644                 r.collapse(true);
41645                 r.pasteHTML(text);
41646                 this.syncValue();
41647                 this.deferFocus();
41648             
41649             }
41650             return;
41651         }
41652         */
41653         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41654             this.win.focus();
41655             
41656             
41657             // from jquery ui (MIT licenced)
41658             var range, node;
41659             var win = this.win;
41660             
41661             if (win.getSelection && win.getSelection().getRangeAt) {
41662                 range = win.getSelection().getRangeAt(0);
41663                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41664                 range.insertNode(node);
41665             } else if (win.document.selection && win.document.selection.createRange) {
41666                 // no firefox support
41667                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41668                 win.document.selection.createRange().pasteHTML(txt);
41669             } else {
41670                 // no firefox support
41671                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41672                 this.execCmd('InsertHTML', txt);
41673             } 
41674             
41675             this.syncValue();
41676             
41677             this.deferFocus();
41678         }
41679     },
41680  // private
41681     mozKeyPress : function(e){
41682         if(e.ctrlKey){
41683             var c = e.getCharCode(), cmd;
41684           
41685             if(c > 0){
41686                 c = String.fromCharCode(c).toLowerCase();
41687                 switch(c){
41688                     case 'b':
41689                         cmd = 'bold';
41690                         break;
41691                     case 'i':
41692                         cmd = 'italic';
41693                         break;
41694                     
41695                     case 'u':
41696                         cmd = 'underline';
41697                         break;
41698                     
41699                     case 'v':
41700                         this.cleanUpPaste.defer(100, this);
41701                         return;
41702                         
41703                 }
41704                 if(cmd){
41705                     this.win.focus();
41706                     this.execCmd(cmd);
41707                     this.deferFocus();
41708                     e.preventDefault();
41709                 }
41710                 
41711             }
41712         }
41713     },
41714
41715     // private
41716     fixKeys : function(){ // load time branching for fastest keydown performance
41717         if(Roo.isIE){
41718             return function(e){
41719                 var k = e.getKey(), r;
41720                 if(k == e.TAB){
41721                     e.stopEvent();
41722                     r = this.doc.selection.createRange();
41723                     if(r){
41724                         r.collapse(true);
41725                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41726                         this.deferFocus();
41727                     }
41728                     return;
41729                 }
41730                 
41731                 if(k == e.ENTER){
41732                     r = this.doc.selection.createRange();
41733                     if(r){
41734                         var target = r.parentElement();
41735                         if(!target || target.tagName.toLowerCase() != 'li'){
41736                             e.stopEvent();
41737                             r.pasteHTML('<br />');
41738                             r.collapse(false);
41739                             r.select();
41740                         }
41741                     }
41742                 }
41743                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41744                     this.cleanUpPaste.defer(100, this);
41745                     return;
41746                 }
41747                 
41748                 
41749             };
41750         }else if(Roo.isOpera){
41751             return function(e){
41752                 var k = e.getKey();
41753                 if(k == e.TAB){
41754                     e.stopEvent();
41755                     this.win.focus();
41756                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41757                     this.deferFocus();
41758                 }
41759                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41760                     this.cleanUpPaste.defer(100, this);
41761                     return;
41762                 }
41763                 
41764             };
41765         }else if(Roo.isSafari){
41766             return function(e){
41767                 var k = e.getKey();
41768                 
41769                 if(k == e.TAB){
41770                     e.stopEvent();
41771                     this.execCmd('InsertText','\t');
41772                     this.deferFocus();
41773                     return;
41774                 }
41775                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41776                     this.cleanUpPaste.defer(100, this);
41777                     return;
41778                 }
41779                 
41780              };
41781         }
41782     }(),
41783     
41784     getAllAncestors: function()
41785     {
41786         var p = this.getSelectedNode();
41787         var a = [];
41788         if (!p) {
41789             a.push(p); // push blank onto stack..
41790             p = this.getParentElement();
41791         }
41792         
41793         
41794         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41795             a.push(p);
41796             p = p.parentNode;
41797         }
41798         a.push(this.doc.body);
41799         return a;
41800     },
41801     lastSel : false,
41802     lastSelNode : false,
41803     
41804     
41805     getSelection : function() 
41806     {
41807         this.assignDocWin();
41808         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41809     },
41810     
41811     getSelectedNode: function() 
41812     {
41813         // this may only work on Gecko!!!
41814         
41815         // should we cache this!!!!
41816         
41817         
41818         
41819          
41820         var range = this.createRange(this.getSelection()).cloneRange();
41821         
41822         if (Roo.isIE) {
41823             var parent = range.parentElement();
41824             while (true) {
41825                 var testRange = range.duplicate();
41826                 testRange.moveToElementText(parent);
41827                 if (testRange.inRange(range)) {
41828                     break;
41829                 }
41830                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41831                     break;
41832                 }
41833                 parent = parent.parentElement;
41834             }
41835             return parent;
41836         }
41837         
41838         // is ancestor a text element.
41839         var ac =  range.commonAncestorContainer;
41840         if (ac.nodeType == 3) {
41841             ac = ac.parentNode;
41842         }
41843         
41844         var ar = ac.childNodes;
41845          
41846         var nodes = [];
41847         var other_nodes = [];
41848         var has_other_nodes = false;
41849         for (var i=0;i<ar.length;i++) {
41850             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41851                 continue;
41852             }
41853             // fullly contained node.
41854             
41855             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41856                 nodes.push(ar[i]);
41857                 continue;
41858             }
41859             
41860             // probably selected..
41861             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41862                 other_nodes.push(ar[i]);
41863                 continue;
41864             }
41865             // outer..
41866             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41867                 continue;
41868             }
41869             
41870             
41871             has_other_nodes = true;
41872         }
41873         if (!nodes.length && other_nodes.length) {
41874             nodes= other_nodes;
41875         }
41876         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41877             return false;
41878         }
41879         
41880         return nodes[0];
41881     },
41882     createRange: function(sel)
41883     {
41884         // this has strange effects when using with 
41885         // top toolbar - not sure if it's a great idea.
41886         //this.editor.contentWindow.focus();
41887         if (typeof sel != "undefined") {
41888             try {
41889                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41890             } catch(e) {
41891                 return this.doc.createRange();
41892             }
41893         } else {
41894             return this.doc.createRange();
41895         }
41896     },
41897     getParentElement: function()
41898     {
41899         
41900         this.assignDocWin();
41901         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41902         
41903         var range = this.createRange(sel);
41904          
41905         try {
41906             var p = range.commonAncestorContainer;
41907             while (p.nodeType == 3) { // text node
41908                 p = p.parentNode;
41909             }
41910             return p;
41911         } catch (e) {
41912             return null;
41913         }
41914     
41915     },
41916     /***
41917      *
41918      * Range intersection.. the hard stuff...
41919      *  '-1' = before
41920      *  '0' = hits..
41921      *  '1' = after.
41922      *         [ -- selected range --- ]
41923      *   [fail]                        [fail]
41924      *
41925      *    basically..
41926      *      if end is before start or  hits it. fail.
41927      *      if start is after end or hits it fail.
41928      *
41929      *   if either hits (but other is outside. - then it's not 
41930      *   
41931      *    
41932      **/
41933     
41934     
41935     // @see http://www.thismuchiknow.co.uk/?p=64.
41936     rangeIntersectsNode : function(range, node)
41937     {
41938         var nodeRange = node.ownerDocument.createRange();
41939         try {
41940             nodeRange.selectNode(node);
41941         } catch (e) {
41942             nodeRange.selectNodeContents(node);
41943         }
41944     
41945         var rangeStartRange = range.cloneRange();
41946         rangeStartRange.collapse(true);
41947     
41948         var rangeEndRange = range.cloneRange();
41949         rangeEndRange.collapse(false);
41950     
41951         var nodeStartRange = nodeRange.cloneRange();
41952         nodeStartRange.collapse(true);
41953     
41954         var nodeEndRange = nodeRange.cloneRange();
41955         nodeEndRange.collapse(false);
41956     
41957         return rangeStartRange.compareBoundaryPoints(
41958                  Range.START_TO_START, nodeEndRange) == -1 &&
41959                rangeEndRange.compareBoundaryPoints(
41960                  Range.START_TO_START, nodeStartRange) == 1;
41961         
41962          
41963     },
41964     rangeCompareNode : function(range, node)
41965     {
41966         var nodeRange = node.ownerDocument.createRange();
41967         try {
41968             nodeRange.selectNode(node);
41969         } catch (e) {
41970             nodeRange.selectNodeContents(node);
41971         }
41972         
41973         
41974         range.collapse(true);
41975     
41976         nodeRange.collapse(true);
41977      
41978         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41979         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41980          
41981         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41982         
41983         var nodeIsBefore   =  ss == 1;
41984         var nodeIsAfter    = ee == -1;
41985         
41986         if (nodeIsBefore && nodeIsAfter)
41987             return 0; // outer
41988         if (!nodeIsBefore && nodeIsAfter)
41989             return 1; //right trailed.
41990         
41991         if (nodeIsBefore && !nodeIsAfter)
41992             return 2;  // left trailed.
41993         // fully contined.
41994         return 3;
41995     },
41996
41997     // private? - in a new class?
41998     cleanUpPaste :  function()
41999     {
42000         // cleans up the whole document..
42001         Roo.log('cleanuppaste');
42002         
42003         this.cleanUpChildren(this.doc.body);
42004         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42005         if (clean != this.doc.body.innerHTML) {
42006             this.doc.body.innerHTML = clean;
42007         }
42008         
42009     },
42010     
42011     cleanWordChars : function(input) {// change the chars to hex code
42012         var he = Roo.HtmlEditorCore;
42013         
42014         var output = input;
42015         Roo.each(he.swapCodes, function(sw) { 
42016             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42017             
42018             output = output.replace(swapper, sw[1]);
42019         });
42020         
42021         return output;
42022     },
42023     
42024     
42025     cleanUpChildren : function (n)
42026     {
42027         if (!n.childNodes.length) {
42028             return;
42029         }
42030         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42031            this.cleanUpChild(n.childNodes[i]);
42032         }
42033     },
42034     
42035     
42036         
42037     
42038     cleanUpChild : function (node)
42039     {
42040         var ed = this;
42041         //console.log(node);
42042         if (node.nodeName == "#text") {
42043             // clean up silly Windows -- stuff?
42044             return; 
42045         }
42046         if (node.nodeName == "#comment") {
42047             node.parentNode.removeChild(node);
42048             // clean up silly Windows -- stuff?
42049             return; 
42050         }
42051         var lcname = node.tagName.toLowerCase();
42052         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42053         // whitelist of tags..
42054         
42055         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42056             // remove node.
42057             node.parentNode.removeChild(node);
42058             return;
42059             
42060         }
42061         
42062         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42063         
42064         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42065         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42066         
42067         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42068         //    remove_keep_children = true;
42069         //}
42070         
42071         if (remove_keep_children) {
42072             this.cleanUpChildren(node);
42073             // inserts everything just before this node...
42074             while (node.childNodes.length) {
42075                 var cn = node.childNodes[0];
42076                 node.removeChild(cn);
42077                 node.parentNode.insertBefore(cn, node);
42078             }
42079             node.parentNode.removeChild(node);
42080             return;
42081         }
42082         
42083         if (!node.attributes || !node.attributes.length) {
42084             this.cleanUpChildren(node);
42085             return;
42086         }
42087         
42088         function cleanAttr(n,v)
42089         {
42090             
42091             if (v.match(/^\./) || v.match(/^\//)) {
42092                 return;
42093             }
42094             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42095                 return;
42096             }
42097             if (v.match(/^#/)) {
42098                 return;
42099             }
42100 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42101             node.removeAttribute(n);
42102             
42103         }
42104         
42105         var cwhite = this.cwhite;
42106         var cblack = this.cblack;
42107             
42108         function cleanStyle(n,v)
42109         {
42110             if (v.match(/expression/)) { //XSS?? should we even bother..
42111                 node.removeAttribute(n);
42112                 return;
42113             }
42114             
42115             var parts = v.split(/;/);
42116             var clean = [];
42117             
42118             Roo.each(parts, function(p) {
42119                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42120                 if (!p.length) {
42121                     return true;
42122                 }
42123                 var l = p.split(':').shift().replace(/\s+/g,'');
42124                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42125                 
42126                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42127 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42128                     //node.removeAttribute(n);
42129                     return true;
42130                 }
42131                 //Roo.log()
42132                 // only allow 'c whitelisted system attributes'
42133                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42134 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42135                     //node.removeAttribute(n);
42136                     return true;
42137                 }
42138                 
42139                 
42140                  
42141                 
42142                 clean.push(p);
42143                 return true;
42144             });
42145             if (clean.length) { 
42146                 node.setAttribute(n, clean.join(';'));
42147             } else {
42148                 node.removeAttribute(n);
42149             }
42150             
42151         }
42152         
42153         
42154         for (var i = node.attributes.length-1; i > -1 ; i--) {
42155             var a = node.attributes[i];
42156             //console.log(a);
42157             
42158             if (a.name.toLowerCase().substr(0,2)=='on')  {
42159                 node.removeAttribute(a.name);
42160                 continue;
42161             }
42162             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42163                 node.removeAttribute(a.name);
42164                 continue;
42165             }
42166             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42167                 cleanAttr(a.name,a.value); // fixme..
42168                 continue;
42169             }
42170             if (a.name == 'style') {
42171                 cleanStyle(a.name,a.value);
42172                 continue;
42173             }
42174             /// clean up MS crap..
42175             // tecnically this should be a list of valid class'es..
42176             
42177             
42178             if (a.name == 'class') {
42179                 if (a.value.match(/^Mso/)) {
42180                     node.className = '';
42181                 }
42182                 
42183                 if (a.value.match(/body/)) {
42184                     node.className = '';
42185                 }
42186                 continue;
42187             }
42188             
42189             // style cleanup!?
42190             // class cleanup?
42191             
42192         }
42193         
42194         
42195         this.cleanUpChildren(node);
42196         
42197         
42198     },
42199     /**
42200      * Clean up MS wordisms...
42201      */
42202     cleanWord : function(node)
42203     {
42204         var _t = this;
42205         var cleanWordChildren = function()
42206         {
42207             if (!node.childNodes.length) {
42208                 return;
42209             }
42210             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42211                _t.cleanWord(node.childNodes[i]);
42212             }
42213         }
42214         
42215         
42216         if (!node) {
42217             this.cleanWord(this.doc.body);
42218             return;
42219         }
42220         if (node.nodeName == "#text") {
42221             // clean up silly Windows -- stuff?
42222             return; 
42223         }
42224         if (node.nodeName == "#comment") {
42225             node.parentNode.removeChild(node);
42226             // clean up silly Windows -- stuff?
42227             return; 
42228         }
42229         
42230         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42231             node.parentNode.removeChild(node);
42232             return;
42233         }
42234         
42235         // remove - but keep children..
42236         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42237             while (node.childNodes.length) {
42238                 var cn = node.childNodes[0];
42239                 node.removeChild(cn);
42240                 node.parentNode.insertBefore(cn, node);
42241             }
42242             node.parentNode.removeChild(node);
42243             cleanWordChildren();
42244             return;
42245         }
42246         // clean styles
42247         if (node.className.length) {
42248             
42249             var cn = node.className.split(/\W+/);
42250             var cna = [];
42251             Roo.each(cn, function(cls) {
42252                 if (cls.match(/Mso[a-zA-Z]+/)) {
42253                     return;
42254                 }
42255                 cna.push(cls);
42256             });
42257             node.className = cna.length ? cna.join(' ') : '';
42258             if (!cna.length) {
42259                 node.removeAttribute("class");
42260             }
42261         }
42262         
42263         if (node.hasAttribute("lang")) {
42264             node.removeAttribute("lang");
42265         }
42266         
42267         if (node.hasAttribute("style")) {
42268             
42269             var styles = node.getAttribute("style").split(";");
42270             var nstyle = [];
42271             Roo.each(styles, function(s) {
42272                 if (!s.match(/:/)) {
42273                     return;
42274                 }
42275                 var kv = s.split(":");
42276                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42277                     return;
42278                 }
42279                 // what ever is left... we allow.
42280                 nstyle.push(s);
42281             });
42282             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42283             if (!nstyle.length) {
42284                 node.removeAttribute('style');
42285             }
42286         }
42287         
42288         cleanWordChildren();
42289         
42290         
42291     },
42292     domToHTML : function(currentElement, depth, nopadtext) {
42293         
42294         depth = depth || 0;
42295         nopadtext = nopadtext || false;
42296     
42297         if (!currentElement) {
42298             return this.domToHTML(this.doc.body);
42299         }
42300         
42301         //Roo.log(currentElement);
42302         var j;
42303         var allText = false;
42304         var nodeName = currentElement.nodeName;
42305         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42306         
42307         if  (nodeName == '#text') {
42308             return currentElement.nodeValue;
42309         }
42310         
42311         
42312         var ret = '';
42313         if (nodeName != 'BODY') {
42314              
42315             var i = 0;
42316             // Prints the node tagName, such as <A>, <IMG>, etc
42317             if (tagName) {
42318                 var attr = [];
42319                 for(i = 0; i < currentElement.attributes.length;i++) {
42320                     // quoting?
42321                     var aname = currentElement.attributes.item(i).name;
42322                     if (!currentElement.attributes.item(i).value.length) {
42323                         continue;
42324                     }
42325                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42326                 }
42327                 
42328                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42329             } 
42330             else {
42331                 
42332                 // eack
42333             }
42334         } else {
42335             tagName = false;
42336         }
42337         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42338             return ret;
42339         }
42340         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42341             nopadtext = true;
42342         }
42343         
42344         
42345         // Traverse the tree
42346         i = 0;
42347         var currentElementChild = currentElement.childNodes.item(i);
42348         var allText = true;
42349         var innerHTML  = '';
42350         lastnode = '';
42351         while (currentElementChild) {
42352             // Formatting code (indent the tree so it looks nice on the screen)
42353             var nopad = nopadtext;
42354             if (lastnode == 'SPAN') {
42355                 nopad  = true;
42356             }
42357             // text
42358             if  (currentElementChild.nodeName == '#text') {
42359                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42360                 if (!nopad && toadd.length > 80) {
42361                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42362                 }
42363                 innerHTML  += toadd;
42364                 
42365                 i++;
42366                 currentElementChild = currentElement.childNodes.item(i);
42367                 lastNode = '';
42368                 continue;
42369             }
42370             allText = false;
42371             
42372             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42373                 
42374             // Recursively traverse the tree structure of the child node
42375             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42376             lastnode = currentElementChild.nodeName;
42377             i++;
42378             currentElementChild=currentElement.childNodes.item(i);
42379         }
42380         
42381         ret += innerHTML;
42382         
42383         if (!allText) {
42384                 // The remaining code is mostly for formatting the tree
42385             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42386         }
42387         
42388         
42389         if (tagName) {
42390             ret+= "</"+tagName+">";
42391         }
42392         return ret;
42393         
42394     },
42395         
42396     applyBlacklists : function()
42397     {
42398         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42399         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42400         
42401         this.white = [];
42402         this.black = [];
42403         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42404             if (b.indexOf(tag) > -1) {
42405                 return;
42406             }
42407             this.white.push(tag);
42408             
42409         }, this);
42410         
42411         Roo.each(w, function(tag) {
42412             if (b.indexOf(tag) > -1) {
42413                 return;
42414             }
42415             if (this.white.indexOf(tag) > -1) {
42416                 return;
42417             }
42418             this.white.push(tag);
42419             
42420         }, this);
42421         
42422         
42423         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42424             if (w.indexOf(tag) > -1) {
42425                 return;
42426             }
42427             this.black.push(tag);
42428             
42429         }, this);
42430         
42431         Roo.each(b, function(tag) {
42432             if (w.indexOf(tag) > -1) {
42433                 return;
42434             }
42435             if (this.black.indexOf(tag) > -1) {
42436                 return;
42437             }
42438             this.black.push(tag);
42439             
42440         }, this);
42441         
42442         
42443         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42444         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42445         
42446         this.cwhite = [];
42447         this.cblack = [];
42448         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42449             if (b.indexOf(tag) > -1) {
42450                 return;
42451             }
42452             this.cwhite.push(tag);
42453             
42454         }, this);
42455         
42456         Roo.each(w, function(tag) {
42457             if (b.indexOf(tag) > -1) {
42458                 return;
42459             }
42460             if (this.cwhite.indexOf(tag) > -1) {
42461                 return;
42462             }
42463             this.cwhite.push(tag);
42464             
42465         }, this);
42466         
42467         
42468         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42469             if (w.indexOf(tag) > -1) {
42470                 return;
42471             }
42472             this.cblack.push(tag);
42473             
42474         }, this);
42475         
42476         Roo.each(b, function(tag) {
42477             if (w.indexOf(tag) > -1) {
42478                 return;
42479             }
42480             if (this.cblack.indexOf(tag) > -1) {
42481                 return;
42482             }
42483             this.cblack.push(tag);
42484             
42485         }, this);
42486     }
42487     
42488     // hide stuff that is not compatible
42489     /**
42490      * @event blur
42491      * @hide
42492      */
42493     /**
42494      * @event change
42495      * @hide
42496      */
42497     /**
42498      * @event focus
42499      * @hide
42500      */
42501     /**
42502      * @event specialkey
42503      * @hide
42504      */
42505     /**
42506      * @cfg {String} fieldClass @hide
42507      */
42508     /**
42509      * @cfg {String} focusClass @hide
42510      */
42511     /**
42512      * @cfg {String} autoCreate @hide
42513      */
42514     /**
42515      * @cfg {String} inputType @hide
42516      */
42517     /**
42518      * @cfg {String} invalidClass @hide
42519      */
42520     /**
42521      * @cfg {String} invalidText @hide
42522      */
42523     /**
42524      * @cfg {String} msgFx @hide
42525      */
42526     /**
42527      * @cfg {String} validateOnBlur @hide
42528      */
42529 });
42530
42531 Roo.HtmlEditorCore.white = [
42532         'area', 'br', 'img', 'input', 'hr', 'wbr',
42533         
42534        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42535        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42536        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42537        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42538        'table',   'ul',         'xmp', 
42539        
42540        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42541       'thead',   'tr', 
42542      
42543       'dir', 'menu', 'ol', 'ul', 'dl',
42544        
42545       'embed',  'object'
42546 ];
42547
42548
42549 Roo.HtmlEditorCore.black = [
42550     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42551         'applet', // 
42552         'base',   'basefont', 'bgsound', 'blink',  'body', 
42553         'frame',  'frameset', 'head',    'html',   'ilayer', 
42554         'iframe', 'layer',  'link',     'meta',    'object',   
42555         'script', 'style' ,'title',  'xml' // clean later..
42556 ];
42557 Roo.HtmlEditorCore.clean = [
42558     'script', 'style', 'title', 'xml'
42559 ];
42560 Roo.HtmlEditorCore.remove = [
42561     'font'
42562 ];
42563 // attributes..
42564
42565 Roo.HtmlEditorCore.ablack = [
42566     'on'
42567 ];
42568     
42569 Roo.HtmlEditorCore.aclean = [ 
42570     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42571 ];
42572
42573 // protocols..
42574 Roo.HtmlEditorCore.pwhite= [
42575         'http',  'https',  'mailto'
42576 ];
42577
42578 // white listed style attributes.
42579 Roo.HtmlEditorCore.cwhite= [
42580       //  'text-align', /// default is to allow most things..
42581       
42582          
42583 //        'font-size'//??
42584 ];
42585
42586 // black listed style attributes.
42587 Roo.HtmlEditorCore.cblack= [
42588       //  'font-size' -- this can be set by the project 
42589 ];
42590
42591
42592 Roo.HtmlEditorCore.swapCodes   =[ 
42593     [    8211, "--" ], 
42594     [    8212, "--" ], 
42595     [    8216,  "'" ],  
42596     [    8217, "'" ],  
42597     [    8220, '"' ],  
42598     [    8221, '"' ],  
42599     [    8226, "*" ],  
42600     [    8230, "..." ]
42601 ]; 
42602
42603     //<script type="text/javascript">
42604
42605 /*
42606  * Ext JS Library 1.1.1
42607  * Copyright(c) 2006-2007, Ext JS, LLC.
42608  * Licence LGPL
42609  * 
42610  */
42611  
42612  
42613 Roo.form.HtmlEditor = function(config){
42614     
42615     
42616     
42617     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42618     
42619     if (!this.toolbars) {
42620         this.toolbars = [];
42621     }
42622     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42623     
42624     
42625 };
42626
42627 /**
42628  * @class Roo.form.HtmlEditor
42629  * @extends Roo.form.Field
42630  * Provides a lightweight HTML Editor component.
42631  *
42632  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42633  * 
42634  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42635  * supported by this editor.</b><br/><br/>
42636  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42637  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42638  */
42639 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42640     /**
42641      * @cfg {Boolean} clearUp
42642      */
42643     clearUp : true,
42644       /**
42645      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42646      */
42647     toolbars : false,
42648    
42649      /**
42650      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42651      *                        Roo.resizable.
42652      */
42653     resizable : false,
42654      /**
42655      * @cfg {Number} height (in pixels)
42656      */   
42657     height: 300,
42658    /**
42659      * @cfg {Number} width (in pixels)
42660      */   
42661     width: 500,
42662     
42663     /**
42664      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42665      * 
42666      */
42667     stylesheets: false,
42668     
42669     
42670      /**
42671      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42672      * 
42673      */
42674     cblack: false,
42675     /**
42676      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42677      * 
42678      */
42679     cwhite: false,
42680     
42681      /**
42682      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42683      * 
42684      */
42685     black: false,
42686     /**
42687      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42688      * 
42689      */
42690     white: false,
42691     
42692     // id of frame..
42693     frameId: false,
42694     
42695     // private properties
42696     validationEvent : false,
42697     deferHeight: true,
42698     initialized : false,
42699     activated : false,
42700     
42701     onFocus : Roo.emptyFn,
42702     iframePad:3,
42703     hideMode:'offsets',
42704     
42705     actionMode : 'container', // defaults to hiding it...
42706     
42707     defaultAutoCreate : { // modified by initCompnoent..
42708         tag: "textarea",
42709         style:"width:500px;height:300px;",
42710         autocomplete: "off"
42711     },
42712
42713     // private
42714     initComponent : function(){
42715         this.addEvents({
42716             /**
42717              * @event initialize
42718              * Fires when the editor is fully initialized (including the iframe)
42719              * @param {HtmlEditor} this
42720              */
42721             initialize: true,
42722             /**
42723              * @event activate
42724              * Fires when the editor is first receives the focus. Any insertion must wait
42725              * until after this event.
42726              * @param {HtmlEditor} this
42727              */
42728             activate: true,
42729              /**
42730              * @event beforesync
42731              * Fires before the textarea is updated with content from the editor iframe. Return false
42732              * to cancel the sync.
42733              * @param {HtmlEditor} this
42734              * @param {String} html
42735              */
42736             beforesync: true,
42737              /**
42738              * @event beforepush
42739              * Fires before the iframe editor is updated with content from the textarea. Return false
42740              * to cancel the push.
42741              * @param {HtmlEditor} this
42742              * @param {String} html
42743              */
42744             beforepush: true,
42745              /**
42746              * @event sync
42747              * Fires when the textarea is updated with content from the editor iframe.
42748              * @param {HtmlEditor} this
42749              * @param {String} html
42750              */
42751             sync: true,
42752              /**
42753              * @event push
42754              * Fires when the iframe editor is updated with content from the textarea.
42755              * @param {HtmlEditor} this
42756              * @param {String} html
42757              */
42758             push: true,
42759              /**
42760              * @event editmodechange
42761              * Fires when the editor switches edit modes
42762              * @param {HtmlEditor} this
42763              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42764              */
42765             editmodechange: true,
42766             /**
42767              * @event editorevent
42768              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42769              * @param {HtmlEditor} this
42770              */
42771             editorevent: true,
42772             /**
42773              * @event firstfocus
42774              * Fires when on first focus - needed by toolbars..
42775              * @param {HtmlEditor} this
42776              */
42777             firstfocus: true,
42778             /**
42779              * @event autosave
42780              * Auto save the htmlEditor value as a file into Events
42781              * @param {HtmlEditor} this
42782              */
42783             autosave: true,
42784             /**
42785              * @event savedpreview
42786              * preview the saved version of htmlEditor
42787              * @param {HtmlEditor} this
42788              */
42789             savedpreview: true
42790         });
42791         this.defaultAutoCreate =  {
42792             tag: "textarea",
42793             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42794             autocomplete: "off"
42795         };
42796     },
42797
42798     /**
42799      * Protected method that will not generally be called directly. It
42800      * is called when the editor creates its toolbar. Override this method if you need to
42801      * add custom toolbar buttons.
42802      * @param {HtmlEditor} editor
42803      */
42804     createToolbar : function(editor){
42805         Roo.log("create toolbars");
42806         if (!editor.toolbars || !editor.toolbars.length) {
42807             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42808         }
42809         
42810         for (var i =0 ; i < editor.toolbars.length;i++) {
42811             editor.toolbars[i] = Roo.factory(
42812                     typeof(editor.toolbars[i]) == 'string' ?
42813                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42814                 Roo.form.HtmlEditor);
42815             editor.toolbars[i].init(editor);
42816         }
42817          
42818         
42819     },
42820
42821      
42822     // private
42823     onRender : function(ct, position)
42824     {
42825         var _t = this;
42826         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42827         
42828         this.wrap = this.el.wrap({
42829             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42830         });
42831         
42832         this.editorcore.onRender(ct, position);
42833          
42834         if (this.resizable) {
42835             this.resizeEl = new Roo.Resizable(this.wrap, {
42836                 pinned : true,
42837                 wrap: true,
42838                 dynamic : true,
42839                 minHeight : this.height,
42840                 height: this.height,
42841                 handles : this.resizable,
42842                 width: this.width,
42843                 listeners : {
42844                     resize : function(r, w, h) {
42845                         _t.onResize(w,h); // -something
42846                     }
42847                 }
42848             });
42849             
42850         }
42851         this.createToolbar(this);
42852        
42853         
42854         if(!this.width){
42855             this.setSize(this.wrap.getSize());
42856         }
42857         if (this.resizeEl) {
42858             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42859             // should trigger onReize..
42860         }
42861         
42862 //        if(this.autosave && this.w){
42863 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42864 //        }
42865     },
42866
42867     // private
42868     onResize : function(w, h)
42869     {
42870         //Roo.log('resize: ' +w + ',' + h );
42871         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42872         var ew = false;
42873         var eh = false;
42874         
42875         if(this.el ){
42876             if(typeof w == 'number'){
42877                 var aw = w - this.wrap.getFrameWidth('lr');
42878                 this.el.setWidth(this.adjustWidth('textarea', aw));
42879                 ew = aw;
42880             }
42881             if(typeof h == 'number'){
42882                 var tbh = 0;
42883                 for (var i =0; i < this.toolbars.length;i++) {
42884                     // fixme - ask toolbars for heights?
42885                     tbh += this.toolbars[i].tb.el.getHeight();
42886                     if (this.toolbars[i].footer) {
42887                         tbh += this.toolbars[i].footer.el.getHeight();
42888                     }
42889                 }
42890                 
42891                 
42892                 
42893                 
42894                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42895                 ah -= 5; // knock a few pixes off for look..
42896                 this.el.setHeight(this.adjustWidth('textarea', ah));
42897                 var eh = ah;
42898             }
42899         }
42900         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42901         this.editorcore.onResize(ew,eh);
42902         
42903     },
42904
42905     /**
42906      * Toggles the editor between standard and source edit mode.
42907      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42908      */
42909     toggleSourceEdit : function(sourceEditMode)
42910     {
42911         this.editorcore.toggleSourceEdit(sourceEditMode);
42912         
42913         if(this.editorcore.sourceEditMode){
42914             Roo.log('editor - showing textarea');
42915             
42916 //            Roo.log('in');
42917 //            Roo.log(this.syncValue());
42918             this.editorcore.syncValue();
42919             this.el.removeClass('x-hidden');
42920             this.el.dom.removeAttribute('tabIndex');
42921             this.el.focus();
42922         }else{
42923             Roo.log('editor - hiding textarea');
42924 //            Roo.log('out')
42925 //            Roo.log(this.pushValue()); 
42926             this.editorcore.pushValue();
42927             
42928             this.el.addClass('x-hidden');
42929             this.el.dom.setAttribute('tabIndex', -1);
42930             //this.deferFocus();
42931         }
42932          
42933         this.setSize(this.wrap.getSize());
42934         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42935     },
42936  
42937     // private (for BoxComponent)
42938     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42939
42940     // private (for BoxComponent)
42941     getResizeEl : function(){
42942         return this.wrap;
42943     },
42944
42945     // private (for BoxComponent)
42946     getPositionEl : function(){
42947         return this.wrap;
42948     },
42949
42950     // private
42951     initEvents : function(){
42952         this.originalValue = this.getValue();
42953     },
42954
42955     /**
42956      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42957      * @method
42958      */
42959     markInvalid : Roo.emptyFn,
42960     /**
42961      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42962      * @method
42963      */
42964     clearInvalid : Roo.emptyFn,
42965
42966     setValue : function(v){
42967         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42968         this.editorcore.pushValue();
42969     },
42970
42971      
42972     // private
42973     deferFocus : function(){
42974         this.focus.defer(10, this);
42975     },
42976
42977     // doc'ed in Field
42978     focus : function(){
42979         this.editorcore.focus();
42980         
42981     },
42982       
42983
42984     // private
42985     onDestroy : function(){
42986         
42987         
42988         
42989         if(this.rendered){
42990             
42991             for (var i =0; i < this.toolbars.length;i++) {
42992                 // fixme - ask toolbars for heights?
42993                 this.toolbars[i].onDestroy();
42994             }
42995             
42996             this.wrap.dom.innerHTML = '';
42997             this.wrap.remove();
42998         }
42999     },
43000
43001     // private
43002     onFirstFocus : function(){
43003         //Roo.log("onFirstFocus");
43004         this.editorcore.onFirstFocus();
43005          for (var i =0; i < this.toolbars.length;i++) {
43006             this.toolbars[i].onFirstFocus();
43007         }
43008         
43009     },
43010     
43011     // private
43012     syncValue : function()
43013     {
43014         this.editorcore.syncValue();
43015     },
43016     
43017     pushValue : function()
43018     {
43019         this.editorcore.pushValue();
43020     }
43021      
43022     
43023     // hide stuff that is not compatible
43024     /**
43025      * @event blur
43026      * @hide
43027      */
43028     /**
43029      * @event change
43030      * @hide
43031      */
43032     /**
43033      * @event focus
43034      * @hide
43035      */
43036     /**
43037      * @event specialkey
43038      * @hide
43039      */
43040     /**
43041      * @cfg {String} fieldClass @hide
43042      */
43043     /**
43044      * @cfg {String} focusClass @hide
43045      */
43046     /**
43047      * @cfg {String} autoCreate @hide
43048      */
43049     /**
43050      * @cfg {String} inputType @hide
43051      */
43052     /**
43053      * @cfg {String} invalidClass @hide
43054      */
43055     /**
43056      * @cfg {String} invalidText @hide
43057      */
43058     /**
43059      * @cfg {String} msgFx @hide
43060      */
43061     /**
43062      * @cfg {String} validateOnBlur @hide
43063      */
43064 });
43065  
43066     // <script type="text/javascript">
43067 /*
43068  * Based on
43069  * Ext JS Library 1.1.1
43070  * Copyright(c) 2006-2007, Ext JS, LLC.
43071  *  
43072  
43073  */
43074
43075 /**
43076  * @class Roo.form.HtmlEditorToolbar1
43077  * Basic Toolbar
43078  * 
43079  * Usage:
43080  *
43081  new Roo.form.HtmlEditor({
43082     ....
43083     toolbars : [
43084         new Roo.form.HtmlEditorToolbar1({
43085             disable : { fonts: 1 , format: 1, ..., ... , ...],
43086             btns : [ .... ]
43087         })
43088     }
43089      
43090  * 
43091  * @cfg {Object} disable List of elements to disable..
43092  * @cfg {Array} btns List of additional buttons.
43093  * 
43094  * 
43095  * NEEDS Extra CSS? 
43096  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43097  */
43098  
43099 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43100 {
43101     
43102     Roo.apply(this, config);
43103     
43104     // default disabled, based on 'good practice'..
43105     this.disable = this.disable || {};
43106     Roo.applyIf(this.disable, {
43107         fontSize : true,
43108         colors : true,
43109         specialElements : true
43110     });
43111     
43112     
43113     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43114     // dont call parent... till later.
43115 }
43116
43117 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43118     
43119     tb: false,
43120     
43121     rendered: false,
43122     
43123     editor : false,
43124     editorcore : false,
43125     /**
43126      * @cfg {Object} disable  List of toolbar elements to disable
43127          
43128      */
43129     disable : false,
43130     
43131     
43132      /**
43133      * @cfg {String} createLinkText The default text for the create link prompt
43134      */
43135     createLinkText : 'Please enter the URL for the link:',
43136     /**
43137      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43138      */
43139     defaultLinkValue : 'http:/'+'/',
43140    
43141     
43142       /**
43143      * @cfg {Array} fontFamilies An array of available font families
43144      */
43145     fontFamilies : [
43146         'Arial',
43147         'Courier New',
43148         'Tahoma',
43149         'Times New Roman',
43150         'Verdana'
43151     ],
43152     
43153     specialChars : [
43154            "&#169;",
43155           "&#174;",     
43156           "&#8482;",    
43157           "&#163;" ,    
43158          // "&#8212;",    
43159           "&#8230;",    
43160           "&#247;" ,    
43161         //  "&#225;" ,     ?? a acute?
43162            "&#8364;"    , //Euro
43163        //   "&#8220;"    ,
43164         //  "&#8221;"    ,
43165         //  "&#8226;"    ,
43166           "&#176;"  //   , // degrees
43167
43168          // "&#233;"     , // e ecute
43169          // "&#250;"     , // u ecute?
43170     ],
43171     
43172     specialElements : [
43173         {
43174             text: "Insert Table",
43175             xtype: 'MenuItem',
43176             xns : Roo.Menu,
43177             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43178                 
43179         },
43180         {    
43181             text: "Insert Image",
43182             xtype: 'MenuItem',
43183             xns : Roo.Menu,
43184             ihtml : '<img src="about:blank"/>'
43185             
43186         }
43187         
43188          
43189     ],
43190     
43191     
43192     inputElements : [ 
43193             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43194             "input:submit", "input:button", "select", "textarea", "label" ],
43195     formats : [
43196         ["p"] ,  
43197         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43198         ["pre"],[ "code"], 
43199         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43200         ['div'],['span']
43201     ],
43202     
43203     cleanStyles : [
43204         "font-size"
43205     ],
43206      /**
43207      * @cfg {String} defaultFont default font to use.
43208      */
43209     defaultFont: 'tahoma',
43210    
43211     fontSelect : false,
43212     
43213     
43214     formatCombo : false,
43215     
43216     init : function(editor)
43217     {
43218         this.editor = editor;
43219         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43220         var editorcore = this.editorcore;
43221         
43222         var _t = this;
43223         
43224         var fid = editorcore.frameId;
43225         var etb = this;
43226         function btn(id, toggle, handler){
43227             var xid = fid + '-'+ id ;
43228             return {
43229                 id : xid,
43230                 cmd : id,
43231                 cls : 'x-btn-icon x-edit-'+id,
43232                 enableToggle:toggle !== false,
43233                 scope: _t, // was editor...
43234                 handler:handler||_t.relayBtnCmd,
43235                 clickEvent:'mousedown',
43236                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43237                 tabIndex:-1
43238             };
43239         }
43240         
43241         
43242         
43243         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43244         this.tb = tb;
43245          // stop form submits
43246         tb.el.on('click', function(e){
43247             e.preventDefault(); // what does this do?
43248         });
43249
43250         if(!this.disable.font) { // && !Roo.isSafari){
43251             /* why no safari for fonts 
43252             editor.fontSelect = tb.el.createChild({
43253                 tag:'select',
43254                 tabIndex: -1,
43255                 cls:'x-font-select',
43256                 html: this.createFontOptions()
43257             });
43258             
43259             editor.fontSelect.on('change', function(){
43260                 var font = editor.fontSelect.dom.value;
43261                 editor.relayCmd('fontname', font);
43262                 editor.deferFocus();
43263             }, editor);
43264             
43265             tb.add(
43266                 editor.fontSelect.dom,
43267                 '-'
43268             );
43269             */
43270             
43271         };
43272         if(!this.disable.formats){
43273             this.formatCombo = new Roo.form.ComboBox({
43274                 store: new Roo.data.SimpleStore({
43275                     id : 'tag',
43276                     fields: ['tag'],
43277                     data : this.formats // from states.js
43278                 }),
43279                 blockFocus : true,
43280                 name : '',
43281                 //autoCreate : {tag: "div",  size: "20"},
43282                 displayField:'tag',
43283                 typeAhead: false,
43284                 mode: 'local',
43285                 editable : false,
43286                 triggerAction: 'all',
43287                 emptyText:'Add tag',
43288                 selectOnFocus:true,
43289                 width:135,
43290                 listeners : {
43291                     'select': function(c, r, i) {
43292                         editorcore.insertTag(r.get('tag'));
43293                         editor.focus();
43294                     }
43295                 }
43296
43297             });
43298             tb.addField(this.formatCombo);
43299             
43300         }
43301         
43302         if(!this.disable.format){
43303             tb.add(
43304                 btn('bold'),
43305                 btn('italic'),
43306                 btn('underline')
43307             );
43308         };
43309         if(!this.disable.fontSize){
43310             tb.add(
43311                 '-',
43312                 
43313                 
43314                 btn('increasefontsize', false, editorcore.adjustFont),
43315                 btn('decreasefontsize', false, editorcore.adjustFont)
43316             );
43317         };
43318         
43319         
43320         if(!this.disable.colors){
43321             tb.add(
43322                 '-', {
43323                     id:editorcore.frameId +'-forecolor',
43324                     cls:'x-btn-icon x-edit-forecolor',
43325                     clickEvent:'mousedown',
43326                     tooltip: this.buttonTips['forecolor'] || undefined,
43327                     tabIndex:-1,
43328                     menu : new Roo.menu.ColorMenu({
43329                         allowReselect: true,
43330                         focus: Roo.emptyFn,
43331                         value:'000000',
43332                         plain:true,
43333                         selectHandler: function(cp, color){
43334                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43335                             editor.deferFocus();
43336                         },
43337                         scope: editorcore,
43338                         clickEvent:'mousedown'
43339                     })
43340                 }, {
43341                     id:editorcore.frameId +'backcolor',
43342                     cls:'x-btn-icon x-edit-backcolor',
43343                     clickEvent:'mousedown',
43344                     tooltip: this.buttonTips['backcolor'] || undefined,
43345                     tabIndex:-1,
43346                     menu : new Roo.menu.ColorMenu({
43347                         focus: Roo.emptyFn,
43348                         value:'FFFFFF',
43349                         plain:true,
43350                         allowReselect: true,
43351                         selectHandler: function(cp, color){
43352                             if(Roo.isGecko){
43353                                 editorcore.execCmd('useCSS', false);
43354                                 editorcore.execCmd('hilitecolor', color);
43355                                 editorcore.execCmd('useCSS', true);
43356                                 editor.deferFocus();
43357                             }else{
43358                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43359                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43360                                 editor.deferFocus();
43361                             }
43362                         },
43363                         scope:editorcore,
43364                         clickEvent:'mousedown'
43365                     })
43366                 }
43367             );
43368         };
43369         // now add all the items...
43370         
43371
43372         if(!this.disable.alignments){
43373             tb.add(
43374                 '-',
43375                 btn('justifyleft'),
43376                 btn('justifycenter'),
43377                 btn('justifyright')
43378             );
43379         };
43380
43381         //if(!Roo.isSafari){
43382             if(!this.disable.links){
43383                 tb.add(
43384                     '-',
43385                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43386                 );
43387             };
43388
43389             if(!this.disable.lists){
43390                 tb.add(
43391                     '-',
43392                     btn('insertorderedlist'),
43393                     btn('insertunorderedlist')
43394                 );
43395             }
43396             if(!this.disable.sourceEdit){
43397                 tb.add(
43398                     '-',
43399                     btn('sourceedit', true, function(btn){
43400                         Roo.log(this);
43401                         this.toggleSourceEdit(btn.pressed);
43402                     })
43403                 );
43404             }
43405         //}
43406         
43407         var smenu = { };
43408         // special menu.. - needs to be tidied up..
43409         if (!this.disable.special) {
43410             smenu = {
43411                 text: "&#169;",
43412                 cls: 'x-edit-none',
43413                 
43414                 menu : {
43415                     items : []
43416                 }
43417             };
43418             for (var i =0; i < this.specialChars.length; i++) {
43419                 smenu.menu.items.push({
43420                     
43421                     html: this.specialChars[i],
43422                     handler: function(a,b) {
43423                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43424                         //editor.insertAtCursor(a.html);
43425                         
43426                     },
43427                     tabIndex:-1
43428                 });
43429             }
43430             
43431             
43432             tb.add(smenu);
43433             
43434             
43435         }
43436         
43437         var cmenu = { };
43438         if (!this.disable.cleanStyles) {
43439             cmenu = {
43440                 cls: 'x-btn-icon x-btn-clear',
43441                 
43442                 menu : {
43443                     items : []
43444                 }
43445             };
43446             for (var i =0; i < this.cleanStyles.length; i++) {
43447                 cmenu.menu.items.push({
43448                     actiontype : this.cleanStyles[i],
43449                     html: 'Remove ' + this.cleanStyles[i],
43450                     handler: function(a,b) {
43451                         Roo.log(a);
43452                         Roo.log(b);
43453                         var c = Roo.get(editorcore.doc.body);
43454                         c.select('[style]').each(function(s) {
43455                             s.dom.style.removeProperty(a.actiontype);
43456                         });
43457                         editorcore.syncValue();
43458                     },
43459                     tabIndex:-1
43460                 });
43461             }
43462             cmenu.menu.items.push({
43463                 actiontype : 'word',
43464                 html: 'Remove MS Word Formating',
43465                 handler: function(a,b) {
43466                     editorcore.cleanWord();
43467                     editorcore.syncValue();
43468                 },
43469                 tabIndex:-1
43470             });
43471             
43472             cmenu.menu.items.push({
43473                 actiontype : 'all',
43474                 html: 'Remove All Styles',
43475                 handler: function(a,b) {
43476                     
43477                     var c = Roo.get(editorcore.doc.body);
43478                     c.select('[style]').each(function(s) {
43479                         s.dom.removeAttribute('style');
43480                     });
43481                     editorcore.syncValue();
43482                 },
43483                 tabIndex:-1
43484             });
43485              cmenu.menu.items.push({
43486                 actiontype : 'word',
43487                 html: 'Tidy HTML Source',
43488                 handler: function(a,b) {
43489                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43490                     editorcore.syncValue();
43491                 },
43492                 tabIndex:-1
43493             });
43494             
43495             
43496             tb.add(cmenu);
43497         }
43498          
43499         if (!this.disable.specialElements) {
43500             var semenu = {
43501                 text: "Other;",
43502                 cls: 'x-edit-none',
43503                 menu : {
43504                     items : []
43505                 }
43506             };
43507             for (var i =0; i < this.specialElements.length; i++) {
43508                 semenu.menu.items.push(
43509                     Roo.apply({ 
43510                         handler: function(a,b) {
43511                             editor.insertAtCursor(this.ihtml);
43512                         }
43513                     }, this.specialElements[i])
43514                 );
43515                     
43516             }
43517             
43518             tb.add(semenu);
43519             
43520             
43521         }
43522          
43523         
43524         if (this.btns) {
43525             for(var i =0; i< this.btns.length;i++) {
43526                 var b = Roo.factory(this.btns[i],Roo.form);
43527                 b.cls =  'x-edit-none';
43528                 b.scope = editorcore;
43529                 tb.add(b);
43530             }
43531         
43532         }
43533         
43534         
43535         
43536         // disable everything...
43537         
43538         this.tb.items.each(function(item){
43539            if(item.id != editorcore.frameId+ '-sourceedit'){
43540                 item.disable();
43541             }
43542         });
43543         this.rendered = true;
43544         
43545         // the all the btns;
43546         editor.on('editorevent', this.updateToolbar, this);
43547         // other toolbars need to implement this..
43548         //editor.on('editmodechange', this.updateToolbar, this);
43549     },
43550     
43551     
43552     relayBtnCmd : function(btn) {
43553         this.editorcore.relayCmd(btn.cmd);
43554     },
43555     // private used internally
43556     createLink : function(){
43557         Roo.log("create link?");
43558         var url = prompt(this.createLinkText, this.defaultLinkValue);
43559         if(url && url != 'http:/'+'/'){
43560             this.editorcore.relayCmd('createlink', url);
43561         }
43562     },
43563
43564     
43565     /**
43566      * Protected method that will not generally be called directly. It triggers
43567      * a toolbar update by reading the markup state of the current selection in the editor.
43568      */
43569     updateToolbar: function(){
43570
43571         if(!this.editorcore.activated){
43572             this.editor.onFirstFocus();
43573             return;
43574         }
43575
43576         var btns = this.tb.items.map, 
43577             doc = this.editorcore.doc,
43578             frameId = this.editorcore.frameId;
43579
43580         if(!this.disable.font && !Roo.isSafari){
43581             /*
43582             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43583             if(name != this.fontSelect.dom.value){
43584                 this.fontSelect.dom.value = name;
43585             }
43586             */
43587         }
43588         if(!this.disable.format){
43589             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43590             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43591             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43592         }
43593         if(!this.disable.alignments){
43594             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43595             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43596             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43597         }
43598         if(!Roo.isSafari && !this.disable.lists){
43599             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43600             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43601         }
43602         
43603         var ans = this.editorcore.getAllAncestors();
43604         if (this.formatCombo) {
43605             
43606             
43607             var store = this.formatCombo.store;
43608             this.formatCombo.setValue("");
43609             for (var i =0; i < ans.length;i++) {
43610                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43611                     // select it..
43612                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43613                     break;
43614                 }
43615             }
43616         }
43617         
43618         
43619         
43620         // hides menus... - so this cant be on a menu...
43621         Roo.menu.MenuMgr.hideAll();
43622
43623         //this.editorsyncValue();
43624     },
43625    
43626     
43627     createFontOptions : function(){
43628         var buf = [], fs = this.fontFamilies, ff, lc;
43629         
43630         
43631         
43632         for(var i = 0, len = fs.length; i< len; i++){
43633             ff = fs[i];
43634             lc = ff.toLowerCase();
43635             buf.push(
43636                 '<option value="',lc,'" style="font-family:',ff,';"',
43637                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43638                     ff,
43639                 '</option>'
43640             );
43641         }
43642         return buf.join('');
43643     },
43644     
43645     toggleSourceEdit : function(sourceEditMode){
43646         
43647         Roo.log("toolbar toogle");
43648         if(sourceEditMode === undefined){
43649             sourceEditMode = !this.sourceEditMode;
43650         }
43651         this.sourceEditMode = sourceEditMode === true;
43652         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43653         // just toggle the button?
43654         if(btn.pressed !== this.sourceEditMode){
43655             btn.toggle(this.sourceEditMode);
43656             return;
43657         }
43658         
43659         if(sourceEditMode){
43660             Roo.log("disabling buttons");
43661             this.tb.items.each(function(item){
43662                 if(item.cmd != 'sourceedit'){
43663                     item.disable();
43664                 }
43665             });
43666           
43667         }else{
43668             Roo.log("enabling buttons");
43669             if(this.editorcore.initialized){
43670                 this.tb.items.each(function(item){
43671                     item.enable();
43672                 });
43673             }
43674             
43675         }
43676         Roo.log("calling toggole on editor");
43677         // tell the editor that it's been pressed..
43678         this.editor.toggleSourceEdit(sourceEditMode);
43679        
43680     },
43681      /**
43682      * Object collection of toolbar tooltips for the buttons in the editor. The key
43683      * is the command id associated with that button and the value is a valid QuickTips object.
43684      * For example:
43685 <pre><code>
43686 {
43687     bold : {
43688         title: 'Bold (Ctrl+B)',
43689         text: 'Make the selected text bold.',
43690         cls: 'x-html-editor-tip'
43691     },
43692     italic : {
43693         title: 'Italic (Ctrl+I)',
43694         text: 'Make the selected text italic.',
43695         cls: 'x-html-editor-tip'
43696     },
43697     ...
43698 </code></pre>
43699     * @type Object
43700      */
43701     buttonTips : {
43702         bold : {
43703             title: 'Bold (Ctrl+B)',
43704             text: 'Make the selected text bold.',
43705             cls: 'x-html-editor-tip'
43706         },
43707         italic : {
43708             title: 'Italic (Ctrl+I)',
43709             text: 'Make the selected text italic.',
43710             cls: 'x-html-editor-tip'
43711         },
43712         underline : {
43713             title: 'Underline (Ctrl+U)',
43714             text: 'Underline the selected text.',
43715             cls: 'x-html-editor-tip'
43716         },
43717         increasefontsize : {
43718             title: 'Grow Text',
43719             text: 'Increase the font size.',
43720             cls: 'x-html-editor-tip'
43721         },
43722         decreasefontsize : {
43723             title: 'Shrink Text',
43724             text: 'Decrease the font size.',
43725             cls: 'x-html-editor-tip'
43726         },
43727         backcolor : {
43728             title: 'Text Highlight Color',
43729             text: 'Change the background color of the selected text.',
43730             cls: 'x-html-editor-tip'
43731         },
43732         forecolor : {
43733             title: 'Font Color',
43734             text: 'Change the color of the selected text.',
43735             cls: 'x-html-editor-tip'
43736         },
43737         justifyleft : {
43738             title: 'Align Text Left',
43739             text: 'Align text to the left.',
43740             cls: 'x-html-editor-tip'
43741         },
43742         justifycenter : {
43743             title: 'Center Text',
43744             text: 'Center text in the editor.',
43745             cls: 'x-html-editor-tip'
43746         },
43747         justifyright : {
43748             title: 'Align Text Right',
43749             text: 'Align text to the right.',
43750             cls: 'x-html-editor-tip'
43751         },
43752         insertunorderedlist : {
43753             title: 'Bullet List',
43754             text: 'Start a bulleted list.',
43755             cls: 'x-html-editor-tip'
43756         },
43757         insertorderedlist : {
43758             title: 'Numbered List',
43759             text: 'Start a numbered list.',
43760             cls: 'x-html-editor-tip'
43761         },
43762         createlink : {
43763             title: 'Hyperlink',
43764             text: 'Make the selected text a hyperlink.',
43765             cls: 'x-html-editor-tip'
43766         },
43767         sourceedit : {
43768             title: 'Source Edit',
43769             text: 'Switch to source editing mode.',
43770             cls: 'x-html-editor-tip'
43771         }
43772     },
43773     // private
43774     onDestroy : function(){
43775         if(this.rendered){
43776             
43777             this.tb.items.each(function(item){
43778                 if(item.menu){
43779                     item.menu.removeAll();
43780                     if(item.menu.el){
43781                         item.menu.el.destroy();
43782                     }
43783                 }
43784                 item.destroy();
43785             });
43786              
43787         }
43788     },
43789     onFirstFocus: function() {
43790         this.tb.items.each(function(item){
43791            item.enable();
43792         });
43793     }
43794 });
43795
43796
43797
43798
43799 // <script type="text/javascript">
43800 /*
43801  * Based on
43802  * Ext JS Library 1.1.1
43803  * Copyright(c) 2006-2007, Ext JS, LLC.
43804  *  
43805  
43806  */
43807
43808  
43809 /**
43810  * @class Roo.form.HtmlEditor.ToolbarContext
43811  * Context Toolbar
43812  * 
43813  * Usage:
43814  *
43815  new Roo.form.HtmlEditor({
43816     ....
43817     toolbars : [
43818         { xtype: 'ToolbarStandard', styles : {} }
43819         { xtype: 'ToolbarContext', disable : {} }
43820     ]
43821 })
43822
43823      
43824  * 
43825  * @config : {Object} disable List of elements to disable.. (not done yet.)
43826  * @config : {Object} styles  Map of styles available.
43827  * 
43828  */
43829
43830 Roo.form.HtmlEditor.ToolbarContext = function(config)
43831 {
43832     
43833     Roo.apply(this, config);
43834     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43835     // dont call parent... till later.
43836     this.styles = this.styles || {};
43837 }
43838
43839  
43840
43841 Roo.form.HtmlEditor.ToolbarContext.types = {
43842     'IMG' : {
43843         width : {
43844             title: "Width",
43845             width: 40
43846         },
43847         height:  {
43848             title: "Height",
43849             width: 40
43850         },
43851         align: {
43852             title: "Align",
43853             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43854             width : 80
43855             
43856         },
43857         border: {
43858             title: "Border",
43859             width: 40
43860         },
43861         alt: {
43862             title: "Alt",
43863             width: 120
43864         },
43865         src : {
43866             title: "Src",
43867             width: 220
43868         }
43869         
43870     },
43871     'A' : {
43872         name : {
43873             title: "Name",
43874             width: 50
43875         },
43876         target:  {
43877             title: "Target",
43878             width: 120
43879         },
43880         href:  {
43881             title: "Href",
43882             width: 220
43883         } // border?
43884         
43885     },
43886     'TABLE' : {
43887         rows : {
43888             title: "Rows",
43889             width: 20
43890         },
43891         cols : {
43892             title: "Cols",
43893             width: 20
43894         },
43895         width : {
43896             title: "Width",
43897             width: 40
43898         },
43899         height : {
43900             title: "Height",
43901             width: 40
43902         },
43903         border : {
43904             title: "Border",
43905             width: 20
43906         }
43907     },
43908     'TD' : {
43909         width : {
43910             title: "Width",
43911             width: 40
43912         },
43913         height : {
43914             title: "Height",
43915             width: 40
43916         },   
43917         align: {
43918             title: "Align",
43919             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43920             width: 80
43921         },
43922         valign: {
43923             title: "Valign",
43924             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43925             width: 80
43926         },
43927         colspan: {
43928             title: "Colspan",
43929             width: 20
43930             
43931         },
43932          'font-family'  : {
43933             title : "Font",
43934             style : 'fontFamily',
43935             displayField: 'display',
43936             optname : 'font-family',
43937             width: 140
43938         }
43939     },
43940     'INPUT' : {
43941         name : {
43942             title: "name",
43943             width: 120
43944         },
43945         value : {
43946             title: "Value",
43947             width: 120
43948         },
43949         width : {
43950             title: "Width",
43951             width: 40
43952         }
43953     },
43954     'LABEL' : {
43955         'for' : {
43956             title: "For",
43957             width: 120
43958         }
43959     },
43960     'TEXTAREA' : {
43961           name : {
43962             title: "name",
43963             width: 120
43964         },
43965         rows : {
43966             title: "Rows",
43967             width: 20
43968         },
43969         cols : {
43970             title: "Cols",
43971             width: 20
43972         }
43973     },
43974     'SELECT' : {
43975         name : {
43976             title: "name",
43977             width: 120
43978         },
43979         selectoptions : {
43980             title: "Options",
43981             width: 200
43982         }
43983     },
43984     
43985     // should we really allow this??
43986     // should this just be 
43987     'BODY' : {
43988         title : {
43989             title: "Title",
43990             width: 200,
43991             disabled : true
43992         }
43993     },
43994     'SPAN' : {
43995         'font-family'  : {
43996             title : "Font",
43997             style : 'fontFamily',
43998             displayField: 'display',
43999             optname : 'font-family',
44000             width: 140
44001         }
44002     },
44003     'DIV' : {
44004         'font-family'  : {
44005             title : "Font",
44006             style : 'fontFamily',
44007             displayField: 'display',
44008             optname : 'font-family',
44009             width: 140
44010         }
44011     },
44012      'P' : {
44013         'font-family'  : {
44014             title : "Font",
44015             style : 'fontFamily',
44016             displayField: 'display',
44017             optname : 'font-family',
44018             width: 140
44019         }
44020     },
44021     
44022     '*' : {
44023         // empty..
44024     }
44025
44026 };
44027
44028 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44029 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44030
44031 Roo.form.HtmlEditor.ToolbarContext.options = {
44032         'font-family'  : [ 
44033                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44034                 [ 'Courier New', 'Courier New'],
44035                 [ 'Tahoma', 'Tahoma'],
44036                 [ 'Times New Roman,serif', 'Times'],
44037                 [ 'Verdana','Verdana' ]
44038         ]
44039 };
44040
44041 // fixme - these need to be configurable..
44042  
44043
44044 Roo.form.HtmlEditor.ToolbarContext.types
44045
44046
44047 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44048     
44049     tb: false,
44050     
44051     rendered: false,
44052     
44053     editor : false,
44054     editorcore : false,
44055     /**
44056      * @cfg {Object} disable  List of toolbar elements to disable
44057          
44058      */
44059     disable : false,
44060     /**
44061      * @cfg {Object} styles List of styles 
44062      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44063      *
44064      * These must be defined in the page, so they get rendered correctly..
44065      * .headline { }
44066      * TD.underline { }
44067      * 
44068      */
44069     styles : false,
44070     
44071     options: false,
44072     
44073     toolbars : false,
44074     
44075     init : function(editor)
44076     {
44077         this.editor = editor;
44078         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44079         var editorcore = this.editorcore;
44080         
44081         var fid = editorcore.frameId;
44082         var etb = this;
44083         function btn(id, toggle, handler){
44084             var xid = fid + '-'+ id ;
44085             return {
44086                 id : xid,
44087                 cmd : id,
44088                 cls : 'x-btn-icon x-edit-'+id,
44089                 enableToggle:toggle !== false,
44090                 scope: editorcore, // was editor...
44091                 handler:handler||editorcore.relayBtnCmd,
44092                 clickEvent:'mousedown',
44093                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44094                 tabIndex:-1
44095             };
44096         }
44097         // create a new element.
44098         var wdiv = editor.wrap.createChild({
44099                 tag: 'div'
44100             }, editor.wrap.dom.firstChild.nextSibling, true);
44101         
44102         // can we do this more than once??
44103         
44104          // stop form submits
44105       
44106  
44107         // disable everything...
44108         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44109         this.toolbars = {};
44110            
44111         for (var i in  ty) {
44112           
44113             this.toolbars[i] = this.buildToolbar(ty[i],i);
44114         }
44115         this.tb = this.toolbars.BODY;
44116         this.tb.el.show();
44117         this.buildFooter();
44118         this.footer.show();
44119         editor.on('hide', function( ) { this.footer.hide() }, this);
44120         editor.on('show', function( ) { this.footer.show() }, this);
44121         
44122          
44123         this.rendered = true;
44124         
44125         // the all the btns;
44126         editor.on('editorevent', this.updateToolbar, this);
44127         // other toolbars need to implement this..
44128         //editor.on('editmodechange', this.updateToolbar, this);
44129     },
44130     
44131     
44132     
44133     /**
44134      * Protected method that will not generally be called directly. It triggers
44135      * a toolbar update by reading the markup state of the current selection in the editor.
44136      */
44137     updateToolbar: function(editor,ev,sel){
44138
44139         //Roo.log(ev);
44140         // capture mouse up - this is handy for selecting images..
44141         // perhaps should go somewhere else...
44142         if(!this.editorcore.activated){
44143              this.editor.onFirstFocus();
44144             return;
44145         }
44146         
44147         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44148         // selectNode - might want to handle IE?
44149         if (ev &&
44150             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44151             ev.target && ev.target.tagName == 'IMG') {
44152             // they have click on an image...
44153             // let's see if we can change the selection...
44154             sel = ev.target;
44155          
44156               var nodeRange = sel.ownerDocument.createRange();
44157             try {
44158                 nodeRange.selectNode(sel);
44159             } catch (e) {
44160                 nodeRange.selectNodeContents(sel);
44161             }
44162             //nodeRange.collapse(true);
44163             var s = this.editorcore.win.getSelection();
44164             s.removeAllRanges();
44165             s.addRange(nodeRange);
44166         }  
44167         
44168       
44169         var updateFooter = sel ? false : true;
44170         
44171         
44172         var ans = this.editorcore.getAllAncestors();
44173         
44174         // pick
44175         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44176         
44177         if (!sel) { 
44178             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44179             sel = sel ? sel : this.editorcore.doc.body;
44180             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44181             
44182         }
44183         // pick a menu that exists..
44184         var tn = sel.tagName.toUpperCase();
44185         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44186         
44187         tn = sel.tagName.toUpperCase();
44188         
44189         var lastSel = this.tb.selectedNode
44190         
44191         this.tb.selectedNode = sel;
44192         
44193         // if current menu does not match..
44194         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44195                 
44196             this.tb.el.hide();
44197             ///console.log("show: " + tn);
44198             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44199             this.tb.el.show();
44200             // update name
44201             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44202             
44203             
44204             // update attributes
44205             if (this.tb.fields) {
44206                 this.tb.fields.each(function(e) {
44207                     if (e.stylename) {
44208                         e.setValue(sel.style[e.stylename]);
44209                         return;
44210                     } 
44211                    e.setValue(sel.getAttribute(e.attrname));
44212                 });
44213             }
44214             
44215             var hasStyles = false;
44216             for(var i in this.styles) {
44217                 hasStyles = true;
44218                 break;
44219             }
44220             
44221             // update styles
44222             if (hasStyles) { 
44223                 var st = this.tb.fields.item(0);
44224                 
44225                 st.store.removeAll();
44226                
44227                 
44228                 var cn = sel.className.split(/\s+/);
44229                 
44230                 var avs = [];
44231                 if (this.styles['*']) {
44232                     
44233                     Roo.each(this.styles['*'], function(v) {
44234                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44235                     });
44236                 }
44237                 if (this.styles[tn]) { 
44238                     Roo.each(this.styles[tn], function(v) {
44239                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44240                     });
44241                 }
44242                 
44243                 st.store.loadData(avs);
44244                 st.collapse();
44245                 st.setValue(cn);
44246             }
44247             // flag our selected Node.
44248             this.tb.selectedNode = sel;
44249            
44250            
44251             Roo.menu.MenuMgr.hideAll();
44252
44253         }
44254         
44255         if (!updateFooter) {
44256             //this.footDisp.dom.innerHTML = ''; 
44257             return;
44258         }
44259         // update the footer
44260         //
44261         var html = '';
44262         
44263         this.footerEls = ans.reverse();
44264         Roo.each(this.footerEls, function(a,i) {
44265             if (!a) { return; }
44266             html += html.length ? ' &gt; '  :  '';
44267             
44268             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44269             
44270         });
44271        
44272         // 
44273         var sz = this.footDisp.up('td').getSize();
44274         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44275         this.footDisp.dom.style.marginLeft = '5px';
44276         
44277         this.footDisp.dom.style.overflow = 'hidden';
44278         
44279         this.footDisp.dom.innerHTML = html;
44280             
44281         //this.editorsyncValue();
44282     },
44283      
44284     
44285    
44286        
44287     // private
44288     onDestroy : function(){
44289         if(this.rendered){
44290             
44291             this.tb.items.each(function(item){
44292                 if(item.menu){
44293                     item.menu.removeAll();
44294                     if(item.menu.el){
44295                         item.menu.el.destroy();
44296                     }
44297                 }
44298                 item.destroy();
44299             });
44300              
44301         }
44302     },
44303     onFirstFocus: function() {
44304         // need to do this for all the toolbars..
44305         this.tb.items.each(function(item){
44306            item.enable();
44307         });
44308     },
44309     buildToolbar: function(tlist, nm)
44310     {
44311         var editor = this.editor;
44312         var editorcore = this.editorcore;
44313          // create a new element.
44314         var wdiv = editor.wrap.createChild({
44315                 tag: 'div'
44316             }, editor.wrap.dom.firstChild.nextSibling, true);
44317         
44318        
44319         var tb = new Roo.Toolbar(wdiv);
44320         // add the name..
44321         
44322         tb.add(nm+ ":&nbsp;");
44323         
44324         var styles = [];
44325         for(var i in this.styles) {
44326             styles.push(i);
44327         }
44328         
44329         // styles...
44330         if (styles && styles.length) {
44331             
44332             // this needs a multi-select checkbox...
44333             tb.addField( new Roo.form.ComboBox({
44334                 store: new Roo.data.SimpleStore({
44335                     id : 'val',
44336                     fields: ['val', 'selected'],
44337                     data : [] 
44338                 }),
44339                 name : '-roo-edit-className',
44340                 attrname : 'className',
44341                 displayField: 'val',
44342                 typeAhead: false,
44343                 mode: 'local',
44344                 editable : false,
44345                 triggerAction: 'all',
44346                 emptyText:'Select Style',
44347                 selectOnFocus:true,
44348                 width: 130,
44349                 listeners : {
44350                     'select': function(c, r, i) {
44351                         // initial support only for on class per el..
44352                         tb.selectedNode.className =  r ? r.get('val') : '';
44353                         editorcore.syncValue();
44354                     }
44355                 }
44356     
44357             }));
44358         }
44359         
44360         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44361         var tbops = tbc.options;
44362         
44363         for (var i in tlist) {
44364             
44365             var item = tlist[i];
44366             tb.add(item.title + ":&nbsp;");
44367             
44368             
44369             //optname == used so you can configure the options available..
44370             var opts = item.opts ? item.opts : false;
44371             if (item.optname) {
44372                 opts = tbops[item.optname];
44373            
44374             }
44375             
44376             if (opts) {
44377                 // opts == pulldown..
44378                 tb.addField( new Roo.form.ComboBox({
44379                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44380                         id : 'val',
44381                         fields: ['val', 'display'],
44382                         data : opts  
44383                     }),
44384                     name : '-roo-edit-' + i,
44385                     attrname : i,
44386                     stylename : item.style ? item.style : false,
44387                     displayField: item.displayField ? item.displayField : 'val',
44388                     valueField :  'val',
44389                     typeAhead: false,
44390                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44391                     editable : false,
44392                     triggerAction: 'all',
44393                     emptyText:'Select',
44394                     selectOnFocus:true,
44395                     width: item.width ? item.width  : 130,
44396                     listeners : {
44397                         'select': function(c, r, i) {
44398                             if (c.stylename) {
44399                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44400                                 return;
44401                             }
44402                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44403                         }
44404                     }
44405
44406                 }));
44407                 continue;
44408                     
44409                  
44410                 
44411                 tb.addField( new Roo.form.TextField({
44412                     name: i,
44413                     width: 100,
44414                     //allowBlank:false,
44415                     value: ''
44416                 }));
44417                 continue;
44418             }
44419             tb.addField( new Roo.form.TextField({
44420                 name: '-roo-edit-' + i,
44421                 attrname : i,
44422                 
44423                 width: item.width,
44424                 //allowBlank:true,
44425                 value: '',
44426                 listeners: {
44427                     'change' : function(f, nv, ov) {
44428                         tb.selectedNode.setAttribute(f.attrname, nv);
44429                     }
44430                 }
44431             }));
44432              
44433         }
44434         tb.addFill();
44435         var _this = this;
44436         tb.addButton( {
44437             text: 'Remove Tag',
44438     
44439             listeners : {
44440                 click : function ()
44441                 {
44442                     // remove
44443                     // undo does not work.
44444                      
44445                     var sn = tb.selectedNode;
44446                     
44447                     var pn = sn.parentNode;
44448                     
44449                     var stn =  sn.childNodes[0];
44450                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44451                     while (sn.childNodes.length) {
44452                         var node = sn.childNodes[0];
44453                         sn.removeChild(node);
44454                         //Roo.log(node);
44455                         pn.insertBefore(node, sn);
44456                         
44457                     }
44458                     pn.removeChild(sn);
44459                     var range = editorcore.createRange();
44460         
44461                     range.setStart(stn,0);
44462                     range.setEnd(en,0); //????
44463                     //range.selectNode(sel);
44464                     
44465                     
44466                     var selection = editorcore.getSelection();
44467                     selection.removeAllRanges();
44468                     selection.addRange(range);
44469                     
44470                     
44471                     
44472                     //_this.updateToolbar(null, null, pn);
44473                     _this.updateToolbar(null, null, null);
44474                     _this.footDisp.dom.innerHTML = ''; 
44475                 }
44476             }
44477             
44478                     
44479                 
44480             
44481         });
44482         
44483         
44484         tb.el.on('click', function(e){
44485             e.preventDefault(); // what does this do?
44486         });
44487         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44488         tb.el.hide();
44489         tb.name = nm;
44490         // dont need to disable them... as they will get hidden
44491         return tb;
44492          
44493         
44494     },
44495     buildFooter : function()
44496     {
44497         
44498         var fel = this.editor.wrap.createChild();
44499         this.footer = new Roo.Toolbar(fel);
44500         // toolbar has scrolly on left / right?
44501         var footDisp= new Roo.Toolbar.Fill();
44502         var _t = this;
44503         this.footer.add(
44504             {
44505                 text : '&lt;',
44506                 xtype: 'Button',
44507                 handler : function() {
44508                     _t.footDisp.scrollTo('left',0,true)
44509                 }
44510             }
44511         );
44512         this.footer.add( footDisp );
44513         this.footer.add( 
44514             {
44515                 text : '&gt;',
44516                 xtype: 'Button',
44517                 handler : function() {
44518                     // no animation..
44519                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44520                 }
44521             }
44522         );
44523         var fel = Roo.get(footDisp.el);
44524         fel.addClass('x-editor-context');
44525         this.footDispWrap = fel; 
44526         this.footDispWrap.overflow  = 'hidden';
44527         
44528         this.footDisp = fel.createChild();
44529         this.footDispWrap.on('click', this.onContextClick, this)
44530         
44531         
44532     },
44533     onContextClick : function (ev,dom)
44534     {
44535         ev.preventDefault();
44536         var  cn = dom.className;
44537         //Roo.log(cn);
44538         if (!cn.match(/x-ed-loc-/)) {
44539             return;
44540         }
44541         var n = cn.split('-').pop();
44542         var ans = this.footerEls;
44543         var sel = ans[n];
44544         
44545          // pick
44546         var range = this.editorcore.createRange();
44547         
44548         range.selectNodeContents(sel);
44549         //range.selectNode(sel);
44550         
44551         
44552         var selection = this.editorcore.getSelection();
44553         selection.removeAllRanges();
44554         selection.addRange(range);
44555         
44556         
44557         
44558         this.updateToolbar(null, null, sel);
44559         
44560         
44561     }
44562     
44563     
44564     
44565     
44566     
44567 });
44568
44569
44570
44571
44572
44573 /*
44574  * Based on:
44575  * Ext JS Library 1.1.1
44576  * Copyright(c) 2006-2007, Ext JS, LLC.
44577  *
44578  * Originally Released Under LGPL - original licence link has changed is not relivant.
44579  *
44580  * Fork - LGPL
44581  * <script type="text/javascript">
44582  */
44583  
44584 /**
44585  * @class Roo.form.BasicForm
44586  * @extends Roo.util.Observable
44587  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44588  * @constructor
44589  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44590  * @param {Object} config Configuration options
44591  */
44592 Roo.form.BasicForm = function(el, config){
44593     this.allItems = [];
44594     this.childForms = [];
44595     Roo.apply(this, config);
44596     /*
44597      * The Roo.form.Field items in this form.
44598      * @type MixedCollection
44599      */
44600      
44601      
44602     this.items = new Roo.util.MixedCollection(false, function(o){
44603         return o.id || (o.id = Roo.id());
44604     });
44605     this.addEvents({
44606         /**
44607          * @event beforeaction
44608          * Fires before any action is performed. Return false to cancel the action.
44609          * @param {Form} this
44610          * @param {Action} action The action to be performed
44611          */
44612         beforeaction: true,
44613         /**
44614          * @event actionfailed
44615          * Fires when an action fails.
44616          * @param {Form} this
44617          * @param {Action} action The action that failed
44618          */
44619         actionfailed : true,
44620         /**
44621          * @event actioncomplete
44622          * Fires when an action is completed.
44623          * @param {Form} this
44624          * @param {Action} action The action that completed
44625          */
44626         actioncomplete : true
44627     });
44628     if(el){
44629         this.initEl(el);
44630     }
44631     Roo.form.BasicForm.superclass.constructor.call(this);
44632 };
44633
44634 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44635     /**
44636      * @cfg {String} method
44637      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44638      */
44639     /**
44640      * @cfg {DataReader} reader
44641      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44642      * This is optional as there is built-in support for processing JSON.
44643      */
44644     /**
44645      * @cfg {DataReader} errorReader
44646      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44647      * This is completely optional as there is built-in support for processing JSON.
44648      */
44649     /**
44650      * @cfg {String} url
44651      * The URL to use for form actions if one isn't supplied in the action options.
44652      */
44653     /**
44654      * @cfg {Boolean} fileUpload
44655      * Set to true if this form is a file upload.
44656      */
44657      
44658     /**
44659      * @cfg {Object} baseParams
44660      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44661      */
44662      /**
44663      
44664     /**
44665      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44666      */
44667     timeout: 30,
44668
44669     // private
44670     activeAction : null,
44671
44672     /**
44673      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44674      * or setValues() data instead of when the form was first created.
44675      */
44676     trackResetOnLoad : false,
44677     
44678     
44679     /**
44680      * childForms - used for multi-tab forms
44681      * @type {Array}
44682      */
44683     childForms : false,
44684     
44685     /**
44686      * allItems - full list of fields.
44687      * @type {Array}
44688      */
44689     allItems : false,
44690     
44691     /**
44692      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44693      * element by passing it or its id or mask the form itself by passing in true.
44694      * @type Mixed
44695      */
44696     waitMsgTarget : false,
44697
44698     // private
44699     initEl : function(el){
44700         this.el = Roo.get(el);
44701         this.id = this.el.id || Roo.id();
44702         this.el.on('submit', this.onSubmit, this);
44703         this.el.addClass('x-form');
44704     },
44705
44706     // private
44707     onSubmit : function(e){
44708         e.stopEvent();
44709     },
44710
44711     /**
44712      * Returns true if client-side validation on the form is successful.
44713      * @return Boolean
44714      */
44715     isValid : function(){
44716         var valid = true;
44717         this.items.each(function(f){
44718            if(!f.validate()){
44719                valid = false;
44720            }
44721         });
44722         return valid;
44723     },
44724
44725     /**
44726      * Returns true if any fields in this form have changed since their original load.
44727      * @return Boolean
44728      */
44729     isDirty : function(){
44730         var dirty = false;
44731         this.items.each(function(f){
44732            if(f.isDirty()){
44733                dirty = true;
44734                return false;
44735            }
44736         });
44737         return dirty;
44738     },
44739
44740     /**
44741      * Performs a predefined action (submit or load) or custom actions you define on this form.
44742      * @param {String} actionName The name of the action type
44743      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44744      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44745      * accept other config options):
44746      * <pre>
44747 Property          Type             Description
44748 ----------------  ---------------  ----------------------------------------------------------------------------------
44749 url               String           The url for the action (defaults to the form's url)
44750 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44751 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44752 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44753                                    validate the form on the client (defaults to false)
44754      * </pre>
44755      * @return {BasicForm} this
44756      */
44757     doAction : function(action, options){
44758         if(typeof action == 'string'){
44759             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44760         }
44761         if(this.fireEvent('beforeaction', this, action) !== false){
44762             this.beforeAction(action);
44763             action.run.defer(100, action);
44764         }
44765         return this;
44766     },
44767
44768     /**
44769      * Shortcut to do a submit action.
44770      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44771      * @return {BasicForm} this
44772      */
44773     submit : function(options){
44774         this.doAction('submit', options);
44775         return this;
44776     },
44777
44778     /**
44779      * Shortcut to do a load action.
44780      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44781      * @return {BasicForm} this
44782      */
44783     load : function(options){
44784         this.doAction('load', options);
44785         return this;
44786     },
44787
44788     /**
44789      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44790      * @param {Record} record The record to edit
44791      * @return {BasicForm} this
44792      */
44793     updateRecord : function(record){
44794         record.beginEdit();
44795         var fs = record.fields;
44796         fs.each(function(f){
44797             var field = this.findField(f.name);
44798             if(field){
44799                 record.set(f.name, field.getValue());
44800             }
44801         }, this);
44802         record.endEdit();
44803         return this;
44804     },
44805
44806     /**
44807      * Loads an Roo.data.Record into this form.
44808      * @param {Record} record The record to load
44809      * @return {BasicForm} this
44810      */
44811     loadRecord : function(record){
44812         this.setValues(record.data);
44813         return this;
44814     },
44815
44816     // private
44817     beforeAction : function(action){
44818         var o = action.options;
44819         
44820        
44821         if(this.waitMsgTarget === true){
44822             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44823         }else if(this.waitMsgTarget){
44824             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44825             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44826         }else {
44827             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44828         }
44829          
44830     },
44831
44832     // private
44833     afterAction : function(action, success){
44834         this.activeAction = null;
44835         var o = action.options;
44836         
44837         if(this.waitMsgTarget === true){
44838             this.el.unmask();
44839         }else if(this.waitMsgTarget){
44840             this.waitMsgTarget.unmask();
44841         }else{
44842             Roo.MessageBox.updateProgress(1);
44843             Roo.MessageBox.hide();
44844         }
44845          
44846         if(success){
44847             if(o.reset){
44848                 this.reset();
44849             }
44850             Roo.callback(o.success, o.scope, [this, action]);
44851             this.fireEvent('actioncomplete', this, action);
44852             
44853         }else{
44854             
44855             // failure condition..
44856             // we have a scenario where updates need confirming.
44857             // eg. if a locking scenario exists..
44858             // we look for { errors : { needs_confirm : true }} in the response.
44859             if (
44860                 (typeof(action.result) != 'undefined')  &&
44861                 (typeof(action.result.errors) != 'undefined')  &&
44862                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44863            ){
44864                 var _t = this;
44865                 Roo.MessageBox.confirm(
44866                     "Change requires confirmation",
44867                     action.result.errorMsg,
44868                     function(r) {
44869                         if (r != 'yes') {
44870                             return;
44871                         }
44872                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44873                     }
44874                     
44875                 );
44876                 
44877                 
44878                 
44879                 return;
44880             }
44881             
44882             Roo.callback(o.failure, o.scope, [this, action]);
44883             // show an error message if no failed handler is set..
44884             if (!this.hasListener('actionfailed')) {
44885                 Roo.MessageBox.alert("Error",
44886                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44887                         action.result.errorMsg :
44888                         "Saving Failed, please check your entries or try again"
44889                 );
44890             }
44891             
44892             this.fireEvent('actionfailed', this, action);
44893         }
44894         
44895     },
44896
44897     /**
44898      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44899      * @param {String} id The value to search for
44900      * @return Field
44901      */
44902     findField : function(id){
44903         var field = this.items.get(id);
44904         if(!field){
44905             this.items.each(function(f){
44906                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44907                     field = f;
44908                     return false;
44909                 }
44910             });
44911         }
44912         return field || null;
44913     },
44914
44915     /**
44916      * Add a secondary form to this one, 
44917      * Used to provide tabbed forms. One form is primary, with hidden values 
44918      * which mirror the elements from the other forms.
44919      * 
44920      * @param {Roo.form.Form} form to add.
44921      * 
44922      */
44923     addForm : function(form)
44924     {
44925        
44926         if (this.childForms.indexOf(form) > -1) {
44927             // already added..
44928             return;
44929         }
44930         this.childForms.push(form);
44931         var n = '';
44932         Roo.each(form.allItems, function (fe) {
44933             
44934             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44935             if (this.findField(n)) { // already added..
44936                 return;
44937             }
44938             var add = new Roo.form.Hidden({
44939                 name : n
44940             });
44941             add.render(this.el);
44942             
44943             this.add( add );
44944         }, this);
44945         
44946     },
44947     /**
44948      * Mark fields in this form invalid in bulk.
44949      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44950      * @return {BasicForm} this
44951      */
44952     markInvalid : function(errors){
44953         if(errors instanceof Array){
44954             for(var i = 0, len = errors.length; i < len; i++){
44955                 var fieldError = errors[i];
44956                 var f = this.findField(fieldError.id);
44957                 if(f){
44958                     f.markInvalid(fieldError.msg);
44959                 }
44960             }
44961         }else{
44962             var field, id;
44963             for(id in errors){
44964                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44965                     field.markInvalid(errors[id]);
44966                 }
44967             }
44968         }
44969         Roo.each(this.childForms || [], function (f) {
44970             f.markInvalid(errors);
44971         });
44972         
44973         return this;
44974     },
44975
44976     /**
44977      * Set values for fields in this form in bulk.
44978      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44979      * @return {BasicForm} this
44980      */
44981     setValues : function(values){
44982         if(values instanceof Array){ // array of objects
44983             for(var i = 0, len = values.length; i < len; i++){
44984                 var v = values[i];
44985                 var f = this.findField(v.id);
44986                 if(f){
44987                     f.setValue(v.value);
44988                     if(this.trackResetOnLoad){
44989                         f.originalValue = f.getValue();
44990                     }
44991                 }
44992             }
44993         }else{ // object hash
44994             var field, id;
44995             for(id in values){
44996                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44997                     
44998                     if (field.setFromData && 
44999                         field.valueField && 
45000                         field.displayField &&
45001                         // combos' with local stores can 
45002                         // be queried via setValue()
45003                         // to set their value..
45004                         (field.store && !field.store.isLocal)
45005                         ) {
45006                         // it's a combo
45007                         var sd = { };
45008                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45009                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45010                         field.setFromData(sd);
45011                         
45012                     } else {
45013                         field.setValue(values[id]);
45014                     }
45015                     
45016                     
45017                     if(this.trackResetOnLoad){
45018                         field.originalValue = field.getValue();
45019                     }
45020                 }
45021             }
45022         }
45023          
45024         Roo.each(this.childForms || [], function (f) {
45025             f.setValues(values);
45026         });
45027                 
45028         return this;
45029     },
45030
45031     /**
45032      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45033      * they are returned as an array.
45034      * @param {Boolean} asString
45035      * @return {Object}
45036      */
45037     getValues : function(asString){
45038         if (this.childForms) {
45039             // copy values from the child forms
45040             Roo.each(this.childForms, function (f) {
45041                 this.setValues(f.getValues());
45042             }, this);
45043         }
45044         
45045         
45046         
45047         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45048         if(asString === true){
45049             return fs;
45050         }
45051         return Roo.urlDecode(fs);
45052     },
45053     
45054     /**
45055      * Returns the fields in this form as an object with key/value pairs. 
45056      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45057      * @return {Object}
45058      */
45059     getFieldValues : function(with_hidden)
45060     {
45061         if (this.childForms) {
45062             // copy values from the child forms
45063             // should this call getFieldValues - probably not as we do not currently copy
45064             // hidden fields when we generate..
45065             Roo.each(this.childForms, function (f) {
45066                 this.setValues(f.getValues());
45067             }, this);
45068         }
45069         
45070         var ret = {};
45071         this.items.each(function(f){
45072             if (!f.getName()) {
45073                 return;
45074             }
45075             var v = f.getValue();
45076             if (f.inputType =='radio') {
45077                 if (typeof(ret[f.getName()]) == 'undefined') {
45078                     ret[f.getName()] = ''; // empty..
45079                 }
45080                 
45081                 if (!f.el.dom.checked) {
45082                     return;
45083                     
45084                 }
45085                 v = f.el.dom.value;
45086                 
45087             }
45088             
45089             // not sure if this supported any more..
45090             if ((typeof(v) == 'object') && f.getRawValue) {
45091                 v = f.getRawValue() ; // dates..
45092             }
45093             // combo boxes where name != hiddenName...
45094             if (f.name != f.getName()) {
45095                 ret[f.name] = f.getRawValue();
45096             }
45097             ret[f.getName()] = v;
45098         });
45099         
45100         return ret;
45101     },
45102
45103     /**
45104      * Clears all invalid messages in this form.
45105      * @return {BasicForm} this
45106      */
45107     clearInvalid : function(){
45108         this.items.each(function(f){
45109            f.clearInvalid();
45110         });
45111         
45112         Roo.each(this.childForms || [], function (f) {
45113             f.clearInvalid();
45114         });
45115         
45116         
45117         return this;
45118     },
45119
45120     /**
45121      * Resets this form.
45122      * @return {BasicForm} this
45123      */
45124     reset : function(){
45125         this.items.each(function(f){
45126             f.reset();
45127         });
45128         
45129         Roo.each(this.childForms || [], function (f) {
45130             f.reset();
45131         });
45132        
45133         
45134         return this;
45135     },
45136
45137     /**
45138      * Add Roo.form components to this form.
45139      * @param {Field} field1
45140      * @param {Field} field2 (optional)
45141      * @param {Field} etc (optional)
45142      * @return {BasicForm} this
45143      */
45144     add : function(){
45145         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45146         return this;
45147     },
45148
45149
45150     /**
45151      * Removes a field from the items collection (does NOT remove its markup).
45152      * @param {Field} field
45153      * @return {BasicForm} this
45154      */
45155     remove : function(field){
45156         this.items.remove(field);
45157         return this;
45158     },
45159
45160     /**
45161      * Looks at the fields in this form, checks them for an id attribute,
45162      * and calls applyTo on the existing dom element with that id.
45163      * @return {BasicForm} this
45164      */
45165     render : function(){
45166         this.items.each(function(f){
45167             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45168                 f.applyTo(f.id);
45169             }
45170         });
45171         return this;
45172     },
45173
45174     /**
45175      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45176      * @param {Object} values
45177      * @return {BasicForm} this
45178      */
45179     applyToFields : function(o){
45180         this.items.each(function(f){
45181            Roo.apply(f, o);
45182         });
45183         return this;
45184     },
45185
45186     /**
45187      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45188      * @param {Object} values
45189      * @return {BasicForm} this
45190      */
45191     applyIfToFields : function(o){
45192         this.items.each(function(f){
45193            Roo.applyIf(f, o);
45194         });
45195         return this;
45196     }
45197 });
45198
45199 // back compat
45200 Roo.BasicForm = Roo.form.BasicForm;/*
45201  * Based on:
45202  * Ext JS Library 1.1.1
45203  * Copyright(c) 2006-2007, Ext JS, LLC.
45204  *
45205  * Originally Released Under LGPL - original licence link has changed is not relivant.
45206  *
45207  * Fork - LGPL
45208  * <script type="text/javascript">
45209  */
45210
45211 /**
45212  * @class Roo.form.Form
45213  * @extends Roo.form.BasicForm
45214  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45215  * @constructor
45216  * @param {Object} config Configuration options
45217  */
45218 Roo.form.Form = function(config){
45219     var xitems =  [];
45220     if (config.items) {
45221         xitems = config.items;
45222         delete config.items;
45223     }
45224    
45225     
45226     Roo.form.Form.superclass.constructor.call(this, null, config);
45227     this.url = this.url || this.action;
45228     if(!this.root){
45229         this.root = new Roo.form.Layout(Roo.applyIf({
45230             id: Roo.id()
45231         }, config));
45232     }
45233     this.active = this.root;
45234     /**
45235      * Array of all the buttons that have been added to this form via {@link addButton}
45236      * @type Array
45237      */
45238     this.buttons = [];
45239     this.allItems = [];
45240     this.addEvents({
45241         /**
45242          * @event clientvalidation
45243          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45244          * @param {Form} this
45245          * @param {Boolean} valid true if the form has passed client-side validation
45246          */
45247         clientvalidation: true,
45248         /**
45249          * @event rendered
45250          * Fires when the form is rendered
45251          * @param {Roo.form.Form} form
45252          */
45253         rendered : true
45254     });
45255     
45256     if (this.progressUrl) {
45257             // push a hidden field onto the list of fields..
45258             this.addxtype( {
45259                     xns: Roo.form, 
45260                     xtype : 'Hidden', 
45261                     name : 'UPLOAD_IDENTIFIER' 
45262             });
45263         }
45264         
45265     
45266     Roo.each(xitems, this.addxtype, this);
45267     
45268     
45269     
45270 };
45271
45272 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45273     /**
45274      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45275      */
45276     /**
45277      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45278      */
45279     /**
45280      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45281      */
45282     buttonAlign:'center',
45283
45284     /**
45285      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45286      */
45287     minButtonWidth:75,
45288
45289     /**
45290      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45291      * This property cascades to child containers if not set.
45292      */
45293     labelAlign:'left',
45294
45295     /**
45296      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45297      * fires a looping event with that state. This is required to bind buttons to the valid
45298      * state using the config value formBind:true on the button.
45299      */
45300     monitorValid : false,
45301
45302     /**
45303      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45304      */
45305     monitorPoll : 200,
45306     
45307     /**
45308      * @cfg {String} progressUrl - Url to return progress data 
45309      */
45310     
45311     progressUrl : false,
45312   
45313     /**
45314      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45315      * fields are added and the column is closed. If no fields are passed the column remains open
45316      * until end() is called.
45317      * @param {Object} config The config to pass to the column
45318      * @param {Field} field1 (optional)
45319      * @param {Field} field2 (optional)
45320      * @param {Field} etc (optional)
45321      * @return Column The column container object
45322      */
45323     column : function(c){
45324         var col = new Roo.form.Column(c);
45325         this.start(col);
45326         if(arguments.length > 1){ // duplicate code required because of Opera
45327             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45328             this.end();
45329         }
45330         return col;
45331     },
45332
45333     /**
45334      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45335      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45336      * until end() is called.
45337      * @param {Object} config The config to pass to the fieldset
45338      * @param {Field} field1 (optional)
45339      * @param {Field} field2 (optional)
45340      * @param {Field} etc (optional)
45341      * @return FieldSet The fieldset container object
45342      */
45343     fieldset : function(c){
45344         var fs = new Roo.form.FieldSet(c);
45345         this.start(fs);
45346         if(arguments.length > 1){ // duplicate code required because of Opera
45347             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45348             this.end();
45349         }
45350         return fs;
45351     },
45352
45353     /**
45354      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45355      * fields are added and the container is closed. If no fields are passed the container remains open
45356      * until end() is called.
45357      * @param {Object} config The config to pass to the Layout
45358      * @param {Field} field1 (optional)
45359      * @param {Field} field2 (optional)
45360      * @param {Field} etc (optional)
45361      * @return Layout The container object
45362      */
45363     container : function(c){
45364         var l = new Roo.form.Layout(c);
45365         this.start(l);
45366         if(arguments.length > 1){ // duplicate code required because of Opera
45367             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45368             this.end();
45369         }
45370         return l;
45371     },
45372
45373     /**
45374      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45375      * @param {Object} container A Roo.form.Layout or subclass of Layout
45376      * @return {Form} this
45377      */
45378     start : function(c){
45379         // cascade label info
45380         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45381         this.active.stack.push(c);
45382         c.ownerCt = this.active;
45383         this.active = c;
45384         return this;
45385     },
45386
45387     /**
45388      * Closes the current open container
45389      * @return {Form} this
45390      */
45391     end : function(){
45392         if(this.active == this.root){
45393             return this;
45394         }
45395         this.active = this.active.ownerCt;
45396         return this;
45397     },
45398
45399     /**
45400      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45401      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45402      * as the label of the field.
45403      * @param {Field} field1
45404      * @param {Field} field2 (optional)
45405      * @param {Field} etc. (optional)
45406      * @return {Form} this
45407      */
45408     add : function(){
45409         this.active.stack.push.apply(this.active.stack, arguments);
45410         this.allItems.push.apply(this.allItems,arguments);
45411         var r = [];
45412         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45413             if(a[i].isFormField){
45414                 r.push(a[i]);
45415             }
45416         }
45417         if(r.length > 0){
45418             Roo.form.Form.superclass.add.apply(this, r);
45419         }
45420         return this;
45421     },
45422     
45423
45424     
45425     
45426     
45427      /**
45428      * Find any element that has been added to a form, using it's ID or name
45429      * This can include framesets, columns etc. along with regular fields..
45430      * @param {String} id - id or name to find.
45431      
45432      * @return {Element} e - or false if nothing found.
45433      */
45434     findbyId : function(id)
45435     {
45436         var ret = false;
45437         if (!id) {
45438             return ret;
45439         }
45440         Roo.each(this.allItems, function(f){
45441             if (f.id == id || f.name == id ){
45442                 ret = f;
45443                 return false;
45444             }
45445         });
45446         return ret;
45447     },
45448
45449     
45450     
45451     /**
45452      * Render this form into the passed container. This should only be called once!
45453      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45454      * @return {Form} this
45455      */
45456     render : function(ct)
45457     {
45458         
45459         
45460         
45461         ct = Roo.get(ct);
45462         var o = this.autoCreate || {
45463             tag: 'form',
45464             method : this.method || 'POST',
45465             id : this.id || Roo.id()
45466         };
45467         this.initEl(ct.createChild(o));
45468
45469         this.root.render(this.el);
45470         
45471        
45472              
45473         this.items.each(function(f){
45474             f.render('x-form-el-'+f.id);
45475         });
45476
45477         if(this.buttons.length > 0){
45478             // tables are required to maintain order and for correct IE layout
45479             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45480                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45481                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45482             }}, null, true);
45483             var tr = tb.getElementsByTagName('tr')[0];
45484             for(var i = 0, len = this.buttons.length; i < len; i++) {
45485                 var b = this.buttons[i];
45486                 var td = document.createElement('td');
45487                 td.className = 'x-form-btn-td';
45488                 b.render(tr.appendChild(td));
45489             }
45490         }
45491         if(this.monitorValid){ // initialize after render
45492             this.startMonitoring();
45493         }
45494         this.fireEvent('rendered', this);
45495         return this;
45496     },
45497
45498     /**
45499      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45500      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45501      * object or a valid Roo.DomHelper element config
45502      * @param {Function} handler The function called when the button is clicked
45503      * @param {Object} scope (optional) The scope of the handler function
45504      * @return {Roo.Button}
45505      */
45506     addButton : function(config, handler, scope){
45507         var bc = {
45508             handler: handler,
45509             scope: scope,
45510             minWidth: this.minButtonWidth,
45511             hideParent:true
45512         };
45513         if(typeof config == "string"){
45514             bc.text = config;
45515         }else{
45516             Roo.apply(bc, config);
45517         }
45518         var btn = new Roo.Button(null, bc);
45519         this.buttons.push(btn);
45520         return btn;
45521     },
45522
45523      /**
45524      * Adds a series of form elements (using the xtype property as the factory method.
45525      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45526      * @param {Object} config 
45527      */
45528     
45529     addxtype : function()
45530     {
45531         var ar = Array.prototype.slice.call(arguments, 0);
45532         var ret = false;
45533         for(var i = 0; i < ar.length; i++) {
45534             if (!ar[i]) {
45535                 continue; // skip -- if this happends something invalid got sent, we 
45536                 // should ignore it, as basically that interface element will not show up
45537                 // and that should be pretty obvious!!
45538             }
45539             
45540             if (Roo.form[ar[i].xtype]) {
45541                 ar[i].form = this;
45542                 var fe = Roo.factory(ar[i], Roo.form);
45543                 if (!ret) {
45544                     ret = fe;
45545                 }
45546                 fe.form = this;
45547                 if (fe.store) {
45548                     fe.store.form = this;
45549                 }
45550                 if (fe.isLayout) {  
45551                          
45552                     this.start(fe);
45553                     this.allItems.push(fe);
45554                     if (fe.items && fe.addxtype) {
45555                         fe.addxtype.apply(fe, fe.items);
45556                         delete fe.items;
45557                     }
45558                      this.end();
45559                     continue;
45560                 }
45561                 
45562                 
45563                  
45564                 this.add(fe);
45565               //  console.log('adding ' + ar[i].xtype);
45566             }
45567             if (ar[i].xtype == 'Button') {  
45568                 //console.log('adding button');
45569                 //console.log(ar[i]);
45570                 this.addButton(ar[i]);
45571                 this.allItems.push(fe);
45572                 continue;
45573             }
45574             
45575             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45576                 alert('end is not supported on xtype any more, use items');
45577             //    this.end();
45578             //    //console.log('adding end');
45579             }
45580             
45581         }
45582         return ret;
45583     },
45584     
45585     /**
45586      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45587      * option "monitorValid"
45588      */
45589     startMonitoring : function(){
45590         if(!this.bound){
45591             this.bound = true;
45592             Roo.TaskMgr.start({
45593                 run : this.bindHandler,
45594                 interval : this.monitorPoll || 200,
45595                 scope: this
45596             });
45597         }
45598     },
45599
45600     /**
45601      * Stops monitoring of the valid state of this form
45602      */
45603     stopMonitoring : function(){
45604         this.bound = false;
45605     },
45606
45607     // private
45608     bindHandler : function(){
45609         if(!this.bound){
45610             return false; // stops binding
45611         }
45612         var valid = true;
45613         this.items.each(function(f){
45614             if(!f.isValid(true)){
45615                 valid = false;
45616                 return false;
45617             }
45618         });
45619         for(var i = 0, len = this.buttons.length; i < len; i++){
45620             var btn = this.buttons[i];
45621             if(btn.formBind === true && btn.disabled === valid){
45622                 btn.setDisabled(!valid);
45623             }
45624         }
45625         this.fireEvent('clientvalidation', this, valid);
45626     }
45627     
45628     
45629     
45630     
45631     
45632     
45633     
45634     
45635 });
45636
45637
45638 // back compat
45639 Roo.Form = Roo.form.Form;
45640 /*
45641  * Based on:
45642  * Ext JS Library 1.1.1
45643  * Copyright(c) 2006-2007, Ext JS, LLC.
45644  *
45645  * Originally Released Under LGPL - original licence link has changed is not relivant.
45646  *
45647  * Fork - LGPL
45648  * <script type="text/javascript">
45649  */
45650
45651 // as we use this in bootstrap.
45652 Roo.namespace('Roo.form');
45653  /**
45654  * @class Roo.form.Action
45655  * Internal Class used to handle form actions
45656  * @constructor
45657  * @param {Roo.form.BasicForm} el The form element or its id
45658  * @param {Object} config Configuration options
45659  */
45660
45661  
45662  
45663 // define the action interface
45664 Roo.form.Action = function(form, options){
45665     this.form = form;
45666     this.options = options || {};
45667 };
45668 /**
45669  * Client Validation Failed
45670  * @const 
45671  */
45672 Roo.form.Action.CLIENT_INVALID = 'client';
45673 /**
45674  * Server Validation Failed
45675  * @const 
45676  */
45677 Roo.form.Action.SERVER_INVALID = 'server';
45678  /**
45679  * Connect to Server Failed
45680  * @const 
45681  */
45682 Roo.form.Action.CONNECT_FAILURE = 'connect';
45683 /**
45684  * Reading Data from Server Failed
45685  * @const 
45686  */
45687 Roo.form.Action.LOAD_FAILURE = 'load';
45688
45689 Roo.form.Action.prototype = {
45690     type : 'default',
45691     failureType : undefined,
45692     response : undefined,
45693     result : undefined,
45694
45695     // interface method
45696     run : function(options){
45697
45698     },
45699
45700     // interface method
45701     success : function(response){
45702
45703     },
45704
45705     // interface method
45706     handleResponse : function(response){
45707
45708     },
45709
45710     // default connection failure
45711     failure : function(response){
45712         
45713         this.response = response;
45714         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45715         this.form.afterAction(this, false);
45716     },
45717
45718     processResponse : function(response){
45719         this.response = response;
45720         if(!response.responseText){
45721             return true;
45722         }
45723         this.result = this.handleResponse(response);
45724         return this.result;
45725     },
45726
45727     // utility functions used internally
45728     getUrl : function(appendParams){
45729         var url = this.options.url || this.form.url || this.form.el.dom.action;
45730         if(appendParams){
45731             var p = this.getParams();
45732             if(p){
45733                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45734             }
45735         }
45736         return url;
45737     },
45738
45739     getMethod : function(){
45740         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45741     },
45742
45743     getParams : function(){
45744         var bp = this.form.baseParams;
45745         var p = this.options.params;
45746         if(p){
45747             if(typeof p == "object"){
45748                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45749             }else if(typeof p == 'string' && bp){
45750                 p += '&' + Roo.urlEncode(bp);
45751             }
45752         }else if(bp){
45753             p = Roo.urlEncode(bp);
45754         }
45755         return p;
45756     },
45757
45758     createCallback : function(){
45759         return {
45760             success: this.success,
45761             failure: this.failure,
45762             scope: this,
45763             timeout: (this.form.timeout*1000),
45764             upload: this.form.fileUpload ? this.success : undefined
45765         };
45766     }
45767 };
45768
45769 Roo.form.Action.Submit = function(form, options){
45770     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45771 };
45772
45773 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45774     type : 'submit',
45775
45776     haveProgress : false,
45777     uploadComplete : false,
45778     
45779     // uploadProgress indicator.
45780     uploadProgress : function()
45781     {
45782         if (!this.form.progressUrl) {
45783             return;
45784         }
45785         
45786         if (!this.haveProgress) {
45787             Roo.MessageBox.progress("Uploading", "Uploading");
45788         }
45789         if (this.uploadComplete) {
45790            Roo.MessageBox.hide();
45791            return;
45792         }
45793         
45794         this.haveProgress = true;
45795    
45796         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45797         
45798         var c = new Roo.data.Connection();
45799         c.request({
45800             url : this.form.progressUrl,
45801             params: {
45802                 id : uid
45803             },
45804             method: 'GET',
45805             success : function(req){
45806                //console.log(data);
45807                 var rdata = false;
45808                 var edata;
45809                 try  {
45810                    rdata = Roo.decode(req.responseText)
45811                 } catch (e) {
45812                     Roo.log("Invalid data from server..");
45813                     Roo.log(edata);
45814                     return;
45815                 }
45816                 if (!rdata || !rdata.success) {
45817                     Roo.log(rdata);
45818                     Roo.MessageBox.alert(Roo.encode(rdata));
45819                     return;
45820                 }
45821                 var data = rdata.data;
45822                 
45823                 if (this.uploadComplete) {
45824                    Roo.MessageBox.hide();
45825                    return;
45826                 }
45827                    
45828                 if (data){
45829                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45830                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45831                     );
45832                 }
45833                 this.uploadProgress.defer(2000,this);
45834             },
45835        
45836             failure: function(data) {
45837                 Roo.log('progress url failed ');
45838                 Roo.log(data);
45839             },
45840             scope : this
45841         });
45842            
45843     },
45844     
45845     
45846     run : function()
45847     {
45848         // run get Values on the form, so it syncs any secondary forms.
45849         this.form.getValues();
45850         
45851         var o = this.options;
45852         var method = this.getMethod();
45853         var isPost = method == 'POST';
45854         if(o.clientValidation === false || this.form.isValid()){
45855             
45856             if (this.form.progressUrl) {
45857                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45858                     (new Date() * 1) + '' + Math.random());
45859                     
45860             } 
45861             
45862             
45863             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45864                 form:this.form.el.dom,
45865                 url:this.getUrl(!isPost),
45866                 method: method,
45867                 params:isPost ? this.getParams() : null,
45868                 isUpload: this.form.fileUpload
45869             }));
45870             
45871             this.uploadProgress();
45872
45873         }else if (o.clientValidation !== false){ // client validation failed
45874             this.failureType = Roo.form.Action.CLIENT_INVALID;
45875             this.form.afterAction(this, false);
45876         }
45877     },
45878
45879     success : function(response)
45880     {
45881         this.uploadComplete= true;
45882         if (this.haveProgress) {
45883             Roo.MessageBox.hide();
45884         }
45885         
45886         
45887         var result = this.processResponse(response);
45888         if(result === true || result.success){
45889             this.form.afterAction(this, true);
45890             return;
45891         }
45892         if(result.errors){
45893             this.form.markInvalid(result.errors);
45894             this.failureType = Roo.form.Action.SERVER_INVALID;
45895         }
45896         this.form.afterAction(this, false);
45897     },
45898     failure : function(response)
45899     {
45900         this.uploadComplete= true;
45901         if (this.haveProgress) {
45902             Roo.MessageBox.hide();
45903         }
45904         
45905         this.response = response;
45906         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45907         this.form.afterAction(this, false);
45908     },
45909     
45910     handleResponse : function(response){
45911         if(this.form.errorReader){
45912             var rs = this.form.errorReader.read(response);
45913             var errors = [];
45914             if(rs.records){
45915                 for(var i = 0, len = rs.records.length; i < len; i++) {
45916                     var r = rs.records[i];
45917                     errors[i] = r.data;
45918                 }
45919             }
45920             if(errors.length < 1){
45921                 errors = null;
45922             }
45923             return {
45924                 success : rs.success,
45925                 errors : errors
45926             };
45927         }
45928         var ret = false;
45929         try {
45930             ret = Roo.decode(response.responseText);
45931         } catch (e) {
45932             ret = {
45933                 success: false,
45934                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45935                 errors : []
45936             };
45937         }
45938         return ret;
45939         
45940     }
45941 });
45942
45943
45944 Roo.form.Action.Load = function(form, options){
45945     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45946     this.reader = this.form.reader;
45947 };
45948
45949 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45950     type : 'load',
45951
45952     run : function(){
45953         
45954         Roo.Ajax.request(Roo.apply(
45955                 this.createCallback(), {
45956                     method:this.getMethod(),
45957                     url:this.getUrl(false),
45958                     params:this.getParams()
45959         }));
45960     },
45961
45962     success : function(response){
45963         
45964         var result = this.processResponse(response);
45965         if(result === true || !result.success || !result.data){
45966             this.failureType = Roo.form.Action.LOAD_FAILURE;
45967             this.form.afterAction(this, false);
45968             return;
45969         }
45970         this.form.clearInvalid();
45971         this.form.setValues(result.data);
45972         this.form.afterAction(this, true);
45973     },
45974
45975     handleResponse : function(response){
45976         if(this.form.reader){
45977             var rs = this.form.reader.read(response);
45978             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45979             return {
45980                 success : rs.success,
45981                 data : data
45982             };
45983         }
45984         return Roo.decode(response.responseText);
45985     }
45986 });
45987
45988 Roo.form.Action.ACTION_TYPES = {
45989     'load' : Roo.form.Action.Load,
45990     'submit' : Roo.form.Action.Submit
45991 };/*
45992  * Based on:
45993  * Ext JS Library 1.1.1
45994  * Copyright(c) 2006-2007, Ext JS, LLC.
45995  *
45996  * Originally Released Under LGPL - original licence link has changed is not relivant.
45997  *
45998  * Fork - LGPL
45999  * <script type="text/javascript">
46000  */
46001  
46002 /**
46003  * @class Roo.form.Layout
46004  * @extends Roo.Component
46005  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46006  * @constructor
46007  * @param {Object} config Configuration options
46008  */
46009 Roo.form.Layout = function(config){
46010     var xitems = [];
46011     if (config.items) {
46012         xitems = config.items;
46013         delete config.items;
46014     }
46015     Roo.form.Layout.superclass.constructor.call(this, config);
46016     this.stack = [];
46017     Roo.each(xitems, this.addxtype, this);
46018      
46019 };
46020
46021 Roo.extend(Roo.form.Layout, Roo.Component, {
46022     /**
46023      * @cfg {String/Object} autoCreate
46024      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46025      */
46026     /**
46027      * @cfg {String/Object/Function} style
46028      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46029      * a function which returns such a specification.
46030      */
46031     /**
46032      * @cfg {String} labelAlign
46033      * Valid values are "left," "top" and "right" (defaults to "left")
46034      */
46035     /**
46036      * @cfg {Number} labelWidth
46037      * Fixed width in pixels of all field labels (defaults to undefined)
46038      */
46039     /**
46040      * @cfg {Boolean} clear
46041      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46042      */
46043     clear : true,
46044     /**
46045      * @cfg {String} labelSeparator
46046      * The separator to use after field labels (defaults to ':')
46047      */
46048     labelSeparator : ':',
46049     /**
46050      * @cfg {Boolean} hideLabels
46051      * True to suppress the display of field labels in this layout (defaults to false)
46052      */
46053     hideLabels : false,
46054
46055     // private
46056     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46057     
46058     isLayout : true,
46059     
46060     // private
46061     onRender : function(ct, position){
46062         if(this.el){ // from markup
46063             this.el = Roo.get(this.el);
46064         }else {  // generate
46065             var cfg = this.getAutoCreate();
46066             this.el = ct.createChild(cfg, position);
46067         }
46068         if(this.style){
46069             this.el.applyStyles(this.style);
46070         }
46071         if(this.labelAlign){
46072             this.el.addClass('x-form-label-'+this.labelAlign);
46073         }
46074         if(this.hideLabels){
46075             this.labelStyle = "display:none";
46076             this.elementStyle = "padding-left:0;";
46077         }else{
46078             if(typeof this.labelWidth == 'number'){
46079                 this.labelStyle = "width:"+this.labelWidth+"px;";
46080                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46081             }
46082             if(this.labelAlign == 'top'){
46083                 this.labelStyle = "width:auto;";
46084                 this.elementStyle = "padding-left:0;";
46085             }
46086         }
46087         var stack = this.stack;
46088         var slen = stack.length;
46089         if(slen > 0){
46090             if(!this.fieldTpl){
46091                 var t = new Roo.Template(
46092                     '<div class="x-form-item {5}">',
46093                         '<label for="{0}" style="{2}">{1}{4}</label>',
46094                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46095                         '</div>',
46096                     '</div><div class="x-form-clear-left"></div>'
46097                 );
46098                 t.disableFormats = true;
46099                 t.compile();
46100                 Roo.form.Layout.prototype.fieldTpl = t;
46101             }
46102             for(var i = 0; i < slen; i++) {
46103                 if(stack[i].isFormField){
46104                     this.renderField(stack[i]);
46105                 }else{
46106                     this.renderComponent(stack[i]);
46107                 }
46108             }
46109         }
46110         if(this.clear){
46111             this.el.createChild({cls:'x-form-clear'});
46112         }
46113     },
46114
46115     // private
46116     renderField : function(f){
46117         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46118                f.id, //0
46119                f.fieldLabel, //1
46120                f.labelStyle||this.labelStyle||'', //2
46121                this.elementStyle||'', //3
46122                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46123                f.itemCls||this.itemCls||''  //5
46124        ], true).getPrevSibling());
46125     },
46126
46127     // private
46128     renderComponent : function(c){
46129         c.render(c.isLayout ? this.el : this.el.createChild());    
46130     },
46131     /**
46132      * Adds a object form elements (using the xtype property as the factory method.)
46133      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46134      * @param {Object} config 
46135      */
46136     addxtype : function(o)
46137     {
46138         // create the lement.
46139         o.form = this.form;
46140         var fe = Roo.factory(o, Roo.form);
46141         this.form.allItems.push(fe);
46142         this.stack.push(fe);
46143         
46144         if (fe.isFormField) {
46145             this.form.items.add(fe);
46146         }
46147          
46148         return fe;
46149     }
46150 });
46151
46152 /**
46153  * @class Roo.form.Column
46154  * @extends Roo.form.Layout
46155  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46156  * @constructor
46157  * @param {Object} config Configuration options
46158  */
46159 Roo.form.Column = function(config){
46160     Roo.form.Column.superclass.constructor.call(this, config);
46161 };
46162
46163 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46164     /**
46165      * @cfg {Number/String} width
46166      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46167      */
46168     /**
46169      * @cfg {String/Object} autoCreate
46170      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46171      */
46172
46173     // private
46174     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46175
46176     // private
46177     onRender : function(ct, position){
46178         Roo.form.Column.superclass.onRender.call(this, ct, position);
46179         if(this.width){
46180             this.el.setWidth(this.width);
46181         }
46182     }
46183 });
46184
46185
46186 /**
46187  * @class Roo.form.Row
46188  * @extends Roo.form.Layout
46189  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46190  * @constructor
46191  * @param {Object} config Configuration options
46192  */
46193
46194  
46195 Roo.form.Row = function(config){
46196     Roo.form.Row.superclass.constructor.call(this, config);
46197 };
46198  
46199 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46200       /**
46201      * @cfg {Number/String} width
46202      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46203      */
46204     /**
46205      * @cfg {Number/String} height
46206      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46207      */
46208     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46209     
46210     padWidth : 20,
46211     // private
46212     onRender : function(ct, position){
46213         //console.log('row render');
46214         if(!this.rowTpl){
46215             var t = new Roo.Template(
46216                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46217                     '<label for="{0}" style="{2}">{1}{4}</label>',
46218                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46219                     '</div>',
46220                 '</div>'
46221             );
46222             t.disableFormats = true;
46223             t.compile();
46224             Roo.form.Layout.prototype.rowTpl = t;
46225         }
46226         this.fieldTpl = this.rowTpl;
46227         
46228         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46229         var labelWidth = 100;
46230         
46231         if ((this.labelAlign != 'top')) {
46232             if (typeof this.labelWidth == 'number') {
46233                 labelWidth = this.labelWidth
46234             }
46235             this.padWidth =  20 + labelWidth;
46236             
46237         }
46238         
46239         Roo.form.Column.superclass.onRender.call(this, ct, position);
46240         if(this.width){
46241             this.el.setWidth(this.width);
46242         }
46243         if(this.height){
46244             this.el.setHeight(this.height);
46245         }
46246     },
46247     
46248     // private
46249     renderField : function(f){
46250         f.fieldEl = this.fieldTpl.append(this.el, [
46251                f.id, f.fieldLabel,
46252                f.labelStyle||this.labelStyle||'',
46253                this.elementStyle||'',
46254                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46255                f.itemCls||this.itemCls||'',
46256                f.width ? f.width + this.padWidth : 160 + this.padWidth
46257        ],true);
46258     }
46259 });
46260  
46261
46262 /**
46263  * @class Roo.form.FieldSet
46264  * @extends Roo.form.Layout
46265  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46266  * @constructor
46267  * @param {Object} config Configuration options
46268  */
46269 Roo.form.FieldSet = function(config){
46270     Roo.form.FieldSet.superclass.constructor.call(this, config);
46271 };
46272
46273 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46274     /**
46275      * @cfg {String} legend
46276      * The text to display as the legend for the FieldSet (defaults to '')
46277      */
46278     /**
46279      * @cfg {String/Object} autoCreate
46280      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46281      */
46282
46283     // private
46284     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46285
46286     // private
46287     onRender : function(ct, position){
46288         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46289         if(this.legend){
46290             this.setLegend(this.legend);
46291         }
46292     },
46293
46294     // private
46295     setLegend : function(text){
46296         if(this.rendered){
46297             this.el.child('legend').update(text);
46298         }
46299     }
46300 });/*
46301  * Based on:
46302  * Ext JS Library 1.1.1
46303  * Copyright(c) 2006-2007, Ext JS, LLC.
46304  *
46305  * Originally Released Under LGPL - original licence link has changed is not relivant.
46306  *
46307  * Fork - LGPL
46308  * <script type="text/javascript">
46309  */
46310 /**
46311  * @class Roo.form.VTypes
46312  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46313  * @singleton
46314  */
46315 Roo.form.VTypes = function(){
46316     // closure these in so they are only created once.
46317     var alpha = /^[a-zA-Z_]+$/;
46318     var alphanum = /^[a-zA-Z0-9_]+$/;
46319     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46320     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46321
46322     // All these messages and functions are configurable
46323     return {
46324         /**
46325          * The function used to validate email addresses
46326          * @param {String} value The email address
46327          */
46328         'email' : function(v){
46329             return email.test(v);
46330         },
46331         /**
46332          * The error text to display when the email validation function returns false
46333          * @type String
46334          */
46335         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46336         /**
46337          * The keystroke filter mask to be applied on email input
46338          * @type RegExp
46339          */
46340         'emailMask' : /[a-z0-9_\.\-@]/i,
46341
46342         /**
46343          * The function used to validate URLs
46344          * @param {String} value The URL
46345          */
46346         'url' : function(v){
46347             return url.test(v);
46348         },
46349         /**
46350          * The error text to display when the url validation function returns false
46351          * @type String
46352          */
46353         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46354         
46355         /**
46356          * The function used to validate alpha values
46357          * @param {String} value The value
46358          */
46359         'alpha' : function(v){
46360             return alpha.test(v);
46361         },
46362         /**
46363          * The error text to display when the alpha validation function returns false
46364          * @type String
46365          */
46366         'alphaText' : 'This field should only contain letters and _',
46367         /**
46368          * The keystroke filter mask to be applied on alpha input
46369          * @type RegExp
46370          */
46371         'alphaMask' : /[a-z_]/i,
46372
46373         /**
46374          * The function used to validate alphanumeric values
46375          * @param {String} value The value
46376          */
46377         'alphanum' : function(v){
46378             return alphanum.test(v);
46379         },
46380         /**
46381          * The error text to display when the alphanumeric validation function returns false
46382          * @type String
46383          */
46384         'alphanumText' : 'This field should only contain letters, numbers and _',
46385         /**
46386          * The keystroke filter mask to be applied on alphanumeric input
46387          * @type RegExp
46388          */
46389         'alphanumMask' : /[a-z0-9_]/i
46390     };
46391 }();//<script type="text/javascript">
46392
46393 /**
46394  * @class Roo.form.FCKeditor
46395  * @extends Roo.form.TextArea
46396  * Wrapper around the FCKEditor http://www.fckeditor.net
46397  * @constructor
46398  * Creates a new FCKeditor
46399  * @param {Object} config Configuration options
46400  */
46401 Roo.form.FCKeditor = function(config){
46402     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46403     this.addEvents({
46404          /**
46405          * @event editorinit
46406          * Fired when the editor is initialized - you can add extra handlers here..
46407          * @param {FCKeditor} this
46408          * @param {Object} the FCK object.
46409          */
46410         editorinit : true
46411     });
46412     
46413     
46414 };
46415 Roo.form.FCKeditor.editors = { };
46416 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46417 {
46418     //defaultAutoCreate : {
46419     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46420     //},
46421     // private
46422     /**
46423      * @cfg {Object} fck options - see fck manual for details.
46424      */
46425     fckconfig : false,
46426     
46427     /**
46428      * @cfg {Object} fck toolbar set (Basic or Default)
46429      */
46430     toolbarSet : 'Basic',
46431     /**
46432      * @cfg {Object} fck BasePath
46433      */ 
46434     basePath : '/fckeditor/',
46435     
46436     
46437     frame : false,
46438     
46439     value : '',
46440     
46441    
46442     onRender : function(ct, position)
46443     {
46444         if(!this.el){
46445             this.defaultAutoCreate = {
46446                 tag: "textarea",
46447                 style:"width:300px;height:60px;",
46448                 autocomplete: "off"
46449             };
46450         }
46451         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46452         /*
46453         if(this.grow){
46454             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46455             if(this.preventScrollbars){
46456                 this.el.setStyle("overflow", "hidden");
46457             }
46458             this.el.setHeight(this.growMin);
46459         }
46460         */
46461         //console.log('onrender' + this.getId() );
46462         Roo.form.FCKeditor.editors[this.getId()] = this;
46463          
46464
46465         this.replaceTextarea() ;
46466         
46467     },
46468     
46469     getEditor : function() {
46470         return this.fckEditor;
46471     },
46472     /**
46473      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46474      * @param {Mixed} value The value to set
46475      */
46476     
46477     
46478     setValue : function(value)
46479     {
46480         //console.log('setValue: ' + value);
46481         
46482         if(typeof(value) == 'undefined') { // not sure why this is happending...
46483             return;
46484         }
46485         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46486         
46487         //if(!this.el || !this.getEditor()) {
46488         //    this.value = value;
46489             //this.setValue.defer(100,this,[value]);    
46490         //    return;
46491         //} 
46492         
46493         if(!this.getEditor()) {
46494             return;
46495         }
46496         
46497         this.getEditor().SetData(value);
46498         
46499         //
46500
46501     },
46502
46503     /**
46504      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46505      * @return {Mixed} value The field value
46506      */
46507     getValue : function()
46508     {
46509         
46510         if (this.frame && this.frame.dom.style.display == 'none') {
46511             return Roo.form.FCKeditor.superclass.getValue.call(this);
46512         }
46513         
46514         if(!this.el || !this.getEditor()) {
46515            
46516            // this.getValue.defer(100,this); 
46517             return this.value;
46518         }
46519        
46520         
46521         var value=this.getEditor().GetData();
46522         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46523         return Roo.form.FCKeditor.superclass.getValue.call(this);
46524         
46525
46526     },
46527
46528     /**
46529      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46530      * @return {Mixed} value The field value
46531      */
46532     getRawValue : function()
46533     {
46534         if (this.frame && this.frame.dom.style.display == 'none') {
46535             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46536         }
46537         
46538         if(!this.el || !this.getEditor()) {
46539             //this.getRawValue.defer(100,this); 
46540             return this.value;
46541             return;
46542         }
46543         
46544         
46545         
46546         var value=this.getEditor().GetData();
46547         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46548         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46549          
46550     },
46551     
46552     setSize : function(w,h) {
46553         
46554         
46555         
46556         //if (this.frame && this.frame.dom.style.display == 'none') {
46557         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46558         //    return;
46559         //}
46560         //if(!this.el || !this.getEditor()) {
46561         //    this.setSize.defer(100,this, [w,h]); 
46562         //    return;
46563         //}
46564         
46565         
46566         
46567         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46568         
46569         this.frame.dom.setAttribute('width', w);
46570         this.frame.dom.setAttribute('height', h);
46571         this.frame.setSize(w,h);
46572         
46573     },
46574     
46575     toggleSourceEdit : function(value) {
46576         
46577       
46578          
46579         this.el.dom.style.display = value ? '' : 'none';
46580         this.frame.dom.style.display = value ?  'none' : '';
46581         
46582     },
46583     
46584     
46585     focus: function(tag)
46586     {
46587         if (this.frame.dom.style.display == 'none') {
46588             return Roo.form.FCKeditor.superclass.focus.call(this);
46589         }
46590         if(!this.el || !this.getEditor()) {
46591             this.focus.defer(100,this, [tag]); 
46592             return;
46593         }
46594         
46595         
46596         
46597         
46598         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46599         this.getEditor().Focus();
46600         if (tgs.length) {
46601             if (!this.getEditor().Selection.GetSelection()) {
46602                 this.focus.defer(100,this, [tag]); 
46603                 return;
46604             }
46605             
46606             
46607             var r = this.getEditor().EditorDocument.createRange();
46608             r.setStart(tgs[0],0);
46609             r.setEnd(tgs[0],0);
46610             this.getEditor().Selection.GetSelection().removeAllRanges();
46611             this.getEditor().Selection.GetSelection().addRange(r);
46612             this.getEditor().Focus();
46613         }
46614         
46615     },
46616     
46617     
46618     
46619     replaceTextarea : function()
46620     {
46621         if ( document.getElementById( this.getId() + '___Frame' ) )
46622             return ;
46623         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46624         //{
46625             // We must check the elements firstly using the Id and then the name.
46626         var oTextarea = document.getElementById( this.getId() );
46627         
46628         var colElementsByName = document.getElementsByName( this.getId() ) ;
46629          
46630         oTextarea.style.display = 'none' ;
46631
46632         if ( oTextarea.tabIndex ) {            
46633             this.TabIndex = oTextarea.tabIndex ;
46634         }
46635         
46636         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46637         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46638         this.frame = Roo.get(this.getId() + '___Frame')
46639     },
46640     
46641     _getConfigHtml : function()
46642     {
46643         var sConfig = '' ;
46644
46645         for ( var o in this.fckconfig ) {
46646             sConfig += sConfig.length > 0  ? '&amp;' : '';
46647             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46648         }
46649
46650         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46651     },
46652     
46653     
46654     _getIFrameHtml : function()
46655     {
46656         var sFile = 'fckeditor.html' ;
46657         /* no idea what this is about..
46658         try
46659         {
46660             if ( (/fcksource=true/i).test( window.top.location.search ) )
46661                 sFile = 'fckeditor.original.html' ;
46662         }
46663         catch (e) { 
46664         */
46665
46666         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46667         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46668         
46669         
46670         var html = '<iframe id="' + this.getId() +
46671             '___Frame" src="' + sLink +
46672             '" width="' + this.width +
46673             '" height="' + this.height + '"' +
46674             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46675             ' frameborder="0" scrolling="no"></iframe>' ;
46676
46677         return html ;
46678     },
46679     
46680     _insertHtmlBefore : function( html, element )
46681     {
46682         if ( element.insertAdjacentHTML )       {
46683             // IE
46684             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46685         } else { // Gecko
46686             var oRange = document.createRange() ;
46687             oRange.setStartBefore( element ) ;
46688             var oFragment = oRange.createContextualFragment( html );
46689             element.parentNode.insertBefore( oFragment, element ) ;
46690         }
46691     }
46692     
46693     
46694   
46695     
46696     
46697     
46698     
46699
46700 });
46701
46702 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46703
46704 function FCKeditor_OnComplete(editorInstance){
46705     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46706     f.fckEditor = editorInstance;
46707     //console.log("loaded");
46708     f.fireEvent('editorinit', f, editorInstance);
46709
46710   
46711
46712  
46713
46714
46715
46716
46717
46718
46719
46720
46721
46722
46723
46724
46725
46726
46727
46728 //<script type="text/javascript">
46729 /**
46730  * @class Roo.form.GridField
46731  * @extends Roo.form.Field
46732  * Embed a grid (or editable grid into a form)
46733  * STATUS ALPHA
46734  * 
46735  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46736  * it needs 
46737  * xgrid.store = Roo.data.Store
46738  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46739  * xgrid.store.reader = Roo.data.JsonReader 
46740  * 
46741  * 
46742  * @constructor
46743  * Creates a new GridField
46744  * @param {Object} config Configuration options
46745  */
46746 Roo.form.GridField = function(config){
46747     Roo.form.GridField.superclass.constructor.call(this, config);
46748      
46749 };
46750
46751 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46752     /**
46753      * @cfg {Number} width  - used to restrict width of grid..
46754      */
46755     width : 100,
46756     /**
46757      * @cfg {Number} height - used to restrict height of grid..
46758      */
46759     height : 50,
46760      /**
46761      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46762          * 
46763          *}
46764      */
46765     xgrid : false, 
46766     /**
46767      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46768      * {tag: "input", type: "checkbox", autocomplete: "off"})
46769      */
46770    // defaultAutoCreate : { tag: 'div' },
46771     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46772     /**
46773      * @cfg {String} addTitle Text to include for adding a title.
46774      */
46775     addTitle : false,
46776     //
46777     onResize : function(){
46778         Roo.form.Field.superclass.onResize.apply(this, arguments);
46779     },
46780
46781     initEvents : function(){
46782         // Roo.form.Checkbox.superclass.initEvents.call(this);
46783         // has no events...
46784        
46785     },
46786
46787
46788     getResizeEl : function(){
46789         return this.wrap;
46790     },
46791
46792     getPositionEl : function(){
46793         return this.wrap;
46794     },
46795
46796     // private
46797     onRender : function(ct, position){
46798         
46799         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46800         var style = this.style;
46801         delete this.style;
46802         
46803         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46804         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46805         this.viewEl = this.wrap.createChild({ tag: 'div' });
46806         if (style) {
46807             this.viewEl.applyStyles(style);
46808         }
46809         if (this.width) {
46810             this.viewEl.setWidth(this.width);
46811         }
46812         if (this.height) {
46813             this.viewEl.setHeight(this.height);
46814         }
46815         //if(this.inputValue !== undefined){
46816         //this.setValue(this.value);
46817         
46818         
46819         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46820         
46821         
46822         this.grid.render();
46823         this.grid.getDataSource().on('remove', this.refreshValue, this);
46824         this.grid.getDataSource().on('update', this.refreshValue, this);
46825         this.grid.on('afteredit', this.refreshValue, this);
46826  
46827     },
46828      
46829     
46830     /**
46831      * Sets the value of the item. 
46832      * @param {String} either an object  or a string..
46833      */
46834     setValue : function(v){
46835         //this.value = v;
46836         v = v || []; // empty set..
46837         // this does not seem smart - it really only affects memoryproxy grids..
46838         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46839             var ds = this.grid.getDataSource();
46840             // assumes a json reader..
46841             var data = {}
46842             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46843             ds.loadData( data);
46844         }
46845         // clear selection so it does not get stale.
46846         if (this.grid.sm) { 
46847             this.grid.sm.clearSelections();
46848         }
46849         
46850         Roo.form.GridField.superclass.setValue.call(this, v);
46851         this.refreshValue();
46852         // should load data in the grid really....
46853     },
46854     
46855     // private
46856     refreshValue: function() {
46857          var val = [];
46858         this.grid.getDataSource().each(function(r) {
46859             val.push(r.data);
46860         });
46861         this.el.dom.value = Roo.encode(val);
46862     }
46863     
46864      
46865     
46866     
46867 });/*
46868  * Based on:
46869  * Ext JS Library 1.1.1
46870  * Copyright(c) 2006-2007, Ext JS, LLC.
46871  *
46872  * Originally Released Under LGPL - original licence link has changed is not relivant.
46873  *
46874  * Fork - LGPL
46875  * <script type="text/javascript">
46876  */
46877 /**
46878  * @class Roo.form.DisplayField
46879  * @extends Roo.form.Field
46880  * A generic Field to display non-editable data.
46881  * @constructor
46882  * Creates a new Display Field item.
46883  * @param {Object} config Configuration options
46884  */
46885 Roo.form.DisplayField = function(config){
46886     Roo.form.DisplayField.superclass.constructor.call(this, config);
46887     
46888 };
46889
46890 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46891     inputType:      'hidden',
46892     allowBlank:     true,
46893     readOnly:         true,
46894     
46895  
46896     /**
46897      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46898      */
46899     focusClass : undefined,
46900     /**
46901      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46902      */
46903     fieldClass: 'x-form-field',
46904     
46905      /**
46906      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46907      */
46908     valueRenderer: undefined,
46909     
46910     width: 100,
46911     /**
46912      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46913      * {tag: "input", type: "checkbox", autocomplete: "off"})
46914      */
46915      
46916  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46917
46918     onResize : function(){
46919         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46920         
46921     },
46922
46923     initEvents : function(){
46924         // Roo.form.Checkbox.superclass.initEvents.call(this);
46925         // has no events...
46926        
46927     },
46928
46929
46930     getResizeEl : function(){
46931         return this.wrap;
46932     },
46933
46934     getPositionEl : function(){
46935         return this.wrap;
46936     },
46937
46938     // private
46939     onRender : function(ct, position){
46940         
46941         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46942         //if(this.inputValue !== undefined){
46943         this.wrap = this.el.wrap();
46944         
46945         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46946         
46947         if (this.bodyStyle) {
46948             this.viewEl.applyStyles(this.bodyStyle);
46949         }
46950         //this.viewEl.setStyle('padding', '2px');
46951         
46952         this.setValue(this.value);
46953         
46954     },
46955 /*
46956     // private
46957     initValue : Roo.emptyFn,
46958
46959   */
46960
46961         // private
46962     onClick : function(){
46963         
46964     },
46965
46966     /**
46967      * Sets the checked state of the checkbox.
46968      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46969      */
46970     setValue : function(v){
46971         this.value = v;
46972         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46973         // this might be called before we have a dom element..
46974         if (!this.viewEl) {
46975             return;
46976         }
46977         this.viewEl.dom.innerHTML = html;
46978         Roo.form.DisplayField.superclass.setValue.call(this, v);
46979
46980     }
46981 });/*
46982  * 
46983  * Licence- LGPL
46984  * 
46985  */
46986
46987 /**
46988  * @class Roo.form.DayPicker
46989  * @extends Roo.form.Field
46990  * A Day picker show [M] [T] [W] ....
46991  * @constructor
46992  * Creates a new Day Picker
46993  * @param {Object} config Configuration options
46994  */
46995 Roo.form.DayPicker= function(config){
46996     Roo.form.DayPicker.superclass.constructor.call(this, config);
46997      
46998 };
46999
47000 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47001     /**
47002      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47003      */
47004     focusClass : undefined,
47005     /**
47006      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47007      */
47008     fieldClass: "x-form-field",
47009    
47010     /**
47011      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47012      * {tag: "input", type: "checkbox", autocomplete: "off"})
47013      */
47014     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
47015     
47016    
47017     actionMode : 'viewEl', 
47018     //
47019     // private
47020  
47021     inputType : 'hidden',
47022     
47023      
47024     inputElement: false, // real input element?
47025     basedOn: false, // ????
47026     
47027     isFormField: true, // not sure where this is needed!!!!
47028
47029     onResize : function(){
47030         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47031         if(!this.boxLabel){
47032             this.el.alignTo(this.wrap, 'c-c');
47033         }
47034     },
47035
47036     initEvents : function(){
47037         Roo.form.Checkbox.superclass.initEvents.call(this);
47038         this.el.on("click", this.onClick,  this);
47039         this.el.on("change", this.onClick,  this);
47040     },
47041
47042
47043     getResizeEl : function(){
47044         return this.wrap;
47045     },
47046
47047     getPositionEl : function(){
47048         return this.wrap;
47049     },
47050
47051     
47052     // private
47053     onRender : function(ct, position){
47054         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47055        
47056         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47057         
47058         var r1 = '<table><tr>';
47059         var r2 = '<tr class="x-form-daypick-icons">';
47060         for (var i=0; i < 7; i++) {
47061             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47062             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47063         }
47064         
47065         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47066         viewEl.select('img').on('click', this.onClick, this);
47067         this.viewEl = viewEl;   
47068         
47069         
47070         // this will not work on Chrome!!!
47071         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47072         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47073         
47074         
47075           
47076
47077     },
47078
47079     // private
47080     initValue : Roo.emptyFn,
47081
47082     /**
47083      * Returns the checked state of the checkbox.
47084      * @return {Boolean} True if checked, else false
47085      */
47086     getValue : function(){
47087         return this.el.dom.value;
47088         
47089     },
47090
47091         // private
47092     onClick : function(e){ 
47093         //this.setChecked(!this.checked);
47094         Roo.get(e.target).toggleClass('x-menu-item-checked');
47095         this.refreshValue();
47096         //if(this.el.dom.checked != this.checked){
47097         //    this.setValue(this.el.dom.checked);
47098        // }
47099     },
47100     
47101     // private
47102     refreshValue : function()
47103     {
47104         var val = '';
47105         this.viewEl.select('img',true).each(function(e,i,n)  {
47106             val += e.is(".x-menu-item-checked") ? String(n) : '';
47107         });
47108         this.setValue(val, true);
47109     },
47110
47111     /**
47112      * Sets the checked state of the checkbox.
47113      * On is always based on a string comparison between inputValue and the param.
47114      * @param {Boolean/String} value - the value to set 
47115      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47116      */
47117     setValue : function(v,suppressEvent){
47118         if (!this.el.dom) {
47119             return;
47120         }
47121         var old = this.el.dom.value ;
47122         this.el.dom.value = v;
47123         if (suppressEvent) {
47124             return ;
47125         }
47126          
47127         // update display..
47128         this.viewEl.select('img',true).each(function(e,i,n)  {
47129             
47130             var on = e.is(".x-menu-item-checked");
47131             var newv = v.indexOf(String(n)) > -1;
47132             if (on != newv) {
47133                 e.toggleClass('x-menu-item-checked');
47134             }
47135             
47136         });
47137         
47138         
47139         this.fireEvent('change', this, v, old);
47140         
47141         
47142     },
47143    
47144     // handle setting of hidden value by some other method!!?!?
47145     setFromHidden: function()
47146     {
47147         if(!this.el){
47148             return;
47149         }
47150         //console.log("SET FROM HIDDEN");
47151         //alert('setFrom hidden');
47152         this.setValue(this.el.dom.value);
47153     },
47154     
47155     onDestroy : function()
47156     {
47157         if(this.viewEl){
47158             Roo.get(this.viewEl).remove();
47159         }
47160          
47161         Roo.form.DayPicker.superclass.onDestroy.call(this);
47162     }
47163
47164 });/*
47165  * RooJS Library 1.1.1
47166  * Copyright(c) 2008-2011  Alan Knowles
47167  *
47168  * License - LGPL
47169  */
47170  
47171
47172 /**
47173  * @class Roo.form.ComboCheck
47174  * @extends Roo.form.ComboBox
47175  * A combobox for multiple select items.
47176  *
47177  * FIXME - could do with a reset button..
47178  * 
47179  * @constructor
47180  * Create a new ComboCheck
47181  * @param {Object} config Configuration options
47182  */
47183 Roo.form.ComboCheck = function(config){
47184     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47185     // should verify some data...
47186     // like
47187     // hiddenName = required..
47188     // displayField = required
47189     // valudField == required
47190     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47191     var _t = this;
47192     Roo.each(req, function(e) {
47193         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47194             throw "Roo.form.ComboCheck : missing value for: " + e;
47195         }
47196     });
47197     
47198     
47199 };
47200
47201 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47202      
47203      
47204     editable : false,
47205      
47206     selectedClass: 'x-menu-item-checked', 
47207     
47208     // private
47209     onRender : function(ct, position){
47210         var _t = this;
47211         
47212         
47213         
47214         if(!this.tpl){
47215             var cls = 'x-combo-list';
47216
47217             
47218             this.tpl =  new Roo.Template({
47219                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47220                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47221                    '<span>{' + this.displayField + '}</span>' +
47222                     '</div>' 
47223                 
47224             });
47225         }
47226  
47227         
47228         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47229         this.view.singleSelect = false;
47230         this.view.multiSelect = true;
47231         this.view.toggleSelect = true;
47232         this.pageTb.add(new Roo.Toolbar.Fill(), {
47233             
47234             text: 'Done',
47235             handler: function()
47236             {
47237                 _t.collapse();
47238             }
47239         });
47240     },
47241     
47242     onViewOver : function(e, t){
47243         // do nothing...
47244         return;
47245         
47246     },
47247     
47248     onViewClick : function(doFocus,index){
47249         return;
47250         
47251     },
47252     select: function () {
47253         //Roo.log("SELECT CALLED");
47254     },
47255      
47256     selectByValue : function(xv, scrollIntoView){
47257         var ar = this.getValueArray();
47258         var sels = [];
47259         
47260         Roo.each(ar, function(v) {
47261             if(v === undefined || v === null){
47262                 return;
47263             }
47264             var r = this.findRecord(this.valueField, v);
47265             if(r){
47266                 sels.push(this.store.indexOf(r))
47267                 
47268             }
47269         },this);
47270         this.view.select(sels);
47271         return false;
47272     },
47273     
47274     
47275     
47276     onSelect : function(record, index){
47277        // Roo.log("onselect Called");
47278        // this is only called by the clear button now..
47279         this.view.clearSelections();
47280         this.setValue('[]');
47281         if (this.value != this.valueBefore) {
47282             this.fireEvent('change', this, this.value, this.valueBefore);
47283             this.valueBefore = this.value;
47284         }
47285     },
47286     getValueArray : function()
47287     {
47288         var ar = [] ;
47289         
47290         try {
47291             //Roo.log(this.value);
47292             if (typeof(this.value) == 'undefined') {
47293                 return [];
47294             }
47295             var ar = Roo.decode(this.value);
47296             return  ar instanceof Array ? ar : []; //?? valid?
47297             
47298         } catch(e) {
47299             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47300             return [];
47301         }
47302          
47303     },
47304     expand : function ()
47305     {
47306         
47307         Roo.form.ComboCheck.superclass.expand.call(this);
47308         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47309         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47310         
47311
47312     },
47313     
47314     collapse : function(){
47315         Roo.form.ComboCheck.superclass.collapse.call(this);
47316         var sl = this.view.getSelectedIndexes();
47317         var st = this.store;
47318         var nv = [];
47319         var tv = [];
47320         var r;
47321         Roo.each(sl, function(i) {
47322             r = st.getAt(i);
47323             nv.push(r.get(this.valueField));
47324         },this);
47325         this.setValue(Roo.encode(nv));
47326         if (this.value != this.valueBefore) {
47327
47328             this.fireEvent('change', this, this.value, this.valueBefore);
47329             this.valueBefore = this.value;
47330         }
47331         
47332     },
47333     
47334     setValue : function(v){
47335         // Roo.log(v);
47336         this.value = v;
47337         
47338         var vals = this.getValueArray();
47339         var tv = [];
47340         Roo.each(vals, function(k) {
47341             var r = this.findRecord(this.valueField, k);
47342             if(r){
47343                 tv.push(r.data[this.displayField]);
47344             }else if(this.valueNotFoundText !== undefined){
47345                 tv.push( this.valueNotFoundText );
47346             }
47347         },this);
47348        // Roo.log(tv);
47349         
47350         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47351         this.hiddenField.value = v;
47352         this.value = v;
47353     }
47354     
47355 });/*
47356  * Based on:
47357  * Ext JS Library 1.1.1
47358  * Copyright(c) 2006-2007, Ext JS, LLC.
47359  *
47360  * Originally Released Under LGPL - original licence link has changed is not relivant.
47361  *
47362  * Fork - LGPL
47363  * <script type="text/javascript">
47364  */
47365  
47366 /**
47367  * @class Roo.form.Signature
47368  * @extends Roo.form.Field
47369  * Signature field.  
47370  * @constructor
47371  * 
47372  * @param {Object} config Configuration options
47373  */
47374
47375 Roo.form.Signature = function(config){
47376     Roo.form.Signature.superclass.constructor.call(this, config);
47377     
47378     this.addEvents({// not in used??
47379          /**
47380          * @event confirm
47381          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47382              * @param {Roo.form.Signature} combo This combo box
47383              */
47384         'confirm' : true,
47385         /**
47386          * @event reset
47387          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47388              * @param {Roo.form.ComboBox} combo This combo box
47389              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47390              */
47391         'reset' : true
47392     });
47393 };
47394
47395 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47396     /**
47397      * @cfg {Object} labels Label to use when rendering a form.
47398      * defaults to 
47399      * labels : { 
47400      *      clear : "Clear",
47401      *      confirm : "Confirm"
47402      *  }
47403      */
47404     labels : { 
47405         clear : "Clear",
47406         confirm : "Confirm"
47407     },
47408     /**
47409      * @cfg {Number} width The signature panel width (defaults to 300)
47410      */
47411     width: 300,
47412     /**
47413      * @cfg {Number} height The signature panel height (defaults to 100)
47414      */
47415     height : 100,
47416     /**
47417      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47418      */
47419     allowBlank : false,
47420     
47421     //private
47422     // {Object} signPanel The signature SVG panel element (defaults to {})
47423     signPanel : {},
47424     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47425     isMouseDown : false,
47426     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47427     isConfirmed : false,
47428     // {String} signatureTmp SVG mapping string (defaults to empty string)
47429     signatureTmp : '',
47430     
47431     
47432     defaultAutoCreate : { // modified by initCompnoent..
47433         tag: "input",
47434         type:"hidden"
47435     },
47436
47437     // private
47438     onRender : function(ct, position){
47439         
47440         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47441         
47442         this.wrap = this.el.wrap({
47443             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47444         });
47445         
47446         this.createToolbar(this);
47447         this.signPanel = this.wrap.createChild({
47448                 tag: 'div',
47449                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47450             }, this.el
47451         );
47452             
47453         this.svgID = Roo.id();
47454         this.svgEl = this.signPanel.createChild({
47455               xmlns : 'http://www.w3.org/2000/svg',
47456               tag : 'svg',
47457               id : this.svgID + "-svg",
47458               width: this.width,
47459               height: this.height,
47460               viewBox: '0 0 '+this.width+' '+this.height,
47461               cn : [
47462                 {
47463                     tag: "rect",
47464                     id: this.svgID + "-svg-r",
47465                     width: this.width,
47466                     height: this.height,
47467                     fill: "#ffa"
47468                 },
47469                 {
47470                     tag: "line",
47471                     id: this.svgID + "-svg-l",
47472                     x1: "0", // start
47473                     y1: (this.height*0.8), // start set the line in 80% of height
47474                     x2: this.width, // end
47475                     y2: (this.height*0.8), // end set the line in 80% of height
47476                     'stroke': "#666",
47477                     'stroke-width': "1",
47478                     'stroke-dasharray': "3",
47479                     'shape-rendering': "crispEdges",
47480                     'pointer-events': "none"
47481                 },
47482                 {
47483                     tag: "path",
47484                     id: this.svgID + "-svg-p",
47485                     'stroke': "navy",
47486                     'stroke-width': "3",
47487                     'fill': "none",
47488                     'pointer-events': 'none'
47489                 }
47490               ]
47491         });
47492         this.createSVG();
47493         this.svgBox = this.svgEl.dom.getScreenCTM();
47494     },
47495     createSVG : function(){ 
47496         var svg = this.signPanel;
47497         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47498         var t = this;
47499
47500         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47501         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47502         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47503         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47504         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47505         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47506         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47507         
47508     },
47509     isTouchEvent : function(e){
47510         return e.type.match(/^touch/);
47511     },
47512     getCoords : function (e) {
47513         var pt    = this.svgEl.dom.createSVGPoint();
47514         pt.x = e.clientX; 
47515         pt.y = e.clientY;
47516         if (this.isTouchEvent(e)) {
47517             pt.x =  e.targetTouches[0].clientX 
47518             pt.y = e.targetTouches[0].clientY;
47519         }
47520         var a = this.svgEl.dom.getScreenCTM();
47521         var b = a.inverse();
47522         var mx = pt.matrixTransform(b);
47523         return mx.x + ',' + mx.y;
47524     },
47525     //mouse event headler 
47526     down : function (e) {
47527         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47528         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47529         
47530         this.isMouseDown = true;
47531         
47532         e.preventDefault();
47533     },
47534     move : function (e) {
47535         if (this.isMouseDown) {
47536             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47537             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47538         }
47539         
47540         e.preventDefault();
47541     },
47542     up : function (e) {
47543         this.isMouseDown = false;
47544         var sp = this.signatureTmp.split(' ');
47545         
47546         if(sp.length > 1){
47547             if(!sp[sp.length-2].match(/^L/)){
47548                 sp.pop();
47549                 sp.pop();
47550                 sp.push("");
47551                 this.signatureTmp = sp.join(" ");
47552             }
47553         }
47554         if(this.getValue() != this.signatureTmp){
47555             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47556             this.isConfirmed = false;
47557         }
47558         e.preventDefault();
47559     },
47560     
47561     /**
47562      * Protected method that will not generally be called directly. It
47563      * is called when the editor creates its toolbar. Override this method if you need to
47564      * add custom toolbar buttons.
47565      * @param {HtmlEditor} editor
47566      */
47567     createToolbar : function(editor){
47568          function btn(id, toggle, handler){
47569             var xid = fid + '-'+ id ;
47570             return {
47571                 id : xid,
47572                 cmd : id,
47573                 cls : 'x-btn-icon x-edit-'+id,
47574                 enableToggle:toggle !== false,
47575                 scope: editor, // was editor...
47576                 handler:handler||editor.relayBtnCmd,
47577                 clickEvent:'mousedown',
47578                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47579                 tabIndex:-1
47580             };
47581         }
47582         
47583         
47584         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47585         this.tb = tb;
47586         this.tb.add(
47587            {
47588                 cls : ' x-signature-btn x-signature-'+id,
47589                 scope: editor, // was editor...
47590                 handler: this.reset,
47591                 clickEvent:'mousedown',
47592                 text: this.labels.clear
47593             },
47594             {
47595                  xtype : 'Fill',
47596                  xns: Roo.Toolbar
47597             }, 
47598             {
47599                 cls : '  x-signature-btn x-signature-'+id,
47600                 scope: editor, // was editor...
47601                 handler: this.confirmHandler,
47602                 clickEvent:'mousedown',
47603                 text: this.labels.confirm
47604             }
47605         );
47606     
47607     },
47608     //public
47609     /**
47610      * when user is clicked confirm then show this image.....
47611      * 
47612      * @return {String} Image Data URI
47613      */
47614     getImageDataURI : function(){
47615         var svg = this.svgEl.dom.parentNode.innerHTML;
47616         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47617         return src; 
47618     },
47619     /**
47620      * 
47621      * @return {Boolean} this.isConfirmed
47622      */
47623     getConfirmed : function(){
47624         return this.isConfirmed;
47625     },
47626     /**
47627      * 
47628      * @return {Number} this.width
47629      */
47630     getWidth : function(){
47631         return this.width;
47632     },
47633     /**
47634      * 
47635      * @return {Number} this.height
47636      */
47637     getHeight : function(){
47638         return this.height;
47639     },
47640     // private
47641     getSignature : function(){
47642         return this.signatureTmp;
47643     },
47644     // private
47645     reset : function(){
47646         this.signatureTmp = '';
47647         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47648         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47649         this.isConfirmed = false;
47650         Roo.form.Signature.superclass.reset.call(this);
47651     },
47652     setSignature : function(s){
47653         this.signatureTmp = s;
47654         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47655         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47656         this.setValue(s);
47657         this.isConfirmed = false;
47658         Roo.form.Signature.superclass.reset.call(this);
47659     }, 
47660     test : function(){
47661 //        Roo.log(this.signPanel.dom.contentWindow.up())
47662     },
47663     //private
47664     setConfirmed : function(){
47665         
47666         
47667         
47668 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47669     },
47670     // private
47671     confirmHandler : function(){
47672         if(!this.getSignature()){
47673             return;
47674         }
47675         
47676         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47677         this.setValue(this.getSignature());
47678         this.isConfirmed = true;
47679         
47680         this.fireEvent('confirm', this);
47681     },
47682     // private
47683     // Subclasses should provide the validation implementation by overriding this
47684     validateValue : function(value){
47685         if(this.allowBlank){
47686             return true;
47687         }
47688         
47689         if(this.isConfirmed){
47690             return true;
47691         }
47692         return false;
47693     }
47694 });/*
47695  * Based on:
47696  * Ext JS Library 1.1.1
47697  * Copyright(c) 2006-2007, Ext JS, LLC.
47698  *
47699  * Originally Released Under LGPL - original licence link has changed is not relivant.
47700  *
47701  * Fork - LGPL
47702  * <script type="text/javascript">
47703  */
47704  
47705
47706 /**
47707  * @class Roo.form.ComboBox
47708  * @extends Roo.form.TriggerField
47709  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47710  * @constructor
47711  * Create a new ComboBox.
47712  * @param {Object} config Configuration options
47713  */
47714 Roo.form.Select = function(config){
47715     Roo.form.Select.superclass.constructor.call(this, config);
47716      
47717 };
47718
47719 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47720     /**
47721      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47722      */
47723     /**
47724      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47725      * rendering into an Roo.Editor, defaults to false)
47726      */
47727     /**
47728      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47729      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47730      */
47731     /**
47732      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47733      */
47734     /**
47735      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47736      * the dropdown list (defaults to undefined, with no header element)
47737      */
47738
47739      /**
47740      * @cfg {String/Roo.Template} tpl The template to use to render the output
47741      */
47742      
47743     // private
47744     defaultAutoCreate : {tag: "select"  },
47745     /**
47746      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47747      */
47748     listWidth: undefined,
47749     /**
47750      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47751      * mode = 'remote' or 'text' if mode = 'local')
47752      */
47753     displayField: undefined,
47754     /**
47755      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47756      * mode = 'remote' or 'value' if mode = 'local'). 
47757      * Note: use of a valueField requires the user make a selection
47758      * in order for a value to be mapped.
47759      */
47760     valueField: undefined,
47761     
47762     
47763     /**
47764      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47765      * field's data value (defaults to the underlying DOM element's name)
47766      */
47767     hiddenName: undefined,
47768     /**
47769      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47770      */
47771     listClass: '',
47772     /**
47773      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47774      */
47775     selectedClass: 'x-combo-selected',
47776     /**
47777      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47778      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47779      * which displays a downward arrow icon).
47780      */
47781     triggerClass : 'x-form-arrow-trigger',
47782     /**
47783      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47784      */
47785     shadow:'sides',
47786     /**
47787      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47788      * anchor positions (defaults to 'tl-bl')
47789      */
47790     listAlign: 'tl-bl?',
47791     /**
47792      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47793      */
47794     maxHeight: 300,
47795     /**
47796      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47797      * query specified by the allQuery config option (defaults to 'query')
47798      */
47799     triggerAction: 'query',
47800     /**
47801      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47802      * (defaults to 4, does not apply if editable = false)
47803      */
47804     minChars : 4,
47805     /**
47806      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47807      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47808      */
47809     typeAhead: false,
47810     /**
47811      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47812      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47813      */
47814     queryDelay: 500,
47815     /**
47816      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47817      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47818      */
47819     pageSize: 0,
47820     /**
47821      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47822      * when editable = true (defaults to false)
47823      */
47824     selectOnFocus:false,
47825     /**
47826      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47827      */
47828     queryParam: 'query',
47829     /**
47830      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47831      * when mode = 'remote' (defaults to 'Loading...')
47832      */
47833     loadingText: 'Loading...',
47834     /**
47835      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47836      */
47837     resizable: false,
47838     /**
47839      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47840      */
47841     handleHeight : 8,
47842     /**
47843      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47844      * traditional select (defaults to true)
47845      */
47846     editable: true,
47847     /**
47848      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47849      */
47850     allQuery: '',
47851     /**
47852      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47853      */
47854     mode: 'remote',
47855     /**
47856      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47857      * listWidth has a higher value)
47858      */
47859     minListWidth : 70,
47860     /**
47861      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47862      * allow the user to set arbitrary text into the field (defaults to false)
47863      */
47864     forceSelection:false,
47865     /**
47866      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47867      * if typeAhead = true (defaults to 250)
47868      */
47869     typeAheadDelay : 250,
47870     /**
47871      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47872      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47873      */
47874     valueNotFoundText : undefined,
47875     
47876     /**
47877      * @cfg {String} defaultValue The value displayed after loading the store.
47878      */
47879     defaultValue: '',
47880     
47881     /**
47882      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47883      */
47884     blockFocus : false,
47885     
47886     /**
47887      * @cfg {Boolean} disableClear Disable showing of clear button.
47888      */
47889     disableClear : false,
47890     /**
47891      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47892      */
47893     alwaysQuery : false,
47894     
47895     //private
47896     addicon : false,
47897     editicon: false,
47898     
47899     // element that contains real text value.. (when hidden is used..)
47900      
47901     // private
47902     onRender : function(ct, position){
47903         Roo.form.Field.prototype.onRender.call(this, ct, position);
47904         
47905         if(this.store){
47906             this.store.on('beforeload', this.onBeforeLoad, this);
47907             this.store.on('load', this.onLoad, this);
47908             this.store.on('loadexception', this.onLoadException, this);
47909             this.store.load({});
47910         }
47911         
47912         
47913         
47914     },
47915
47916     // private
47917     initEvents : function(){
47918         //Roo.form.ComboBox.superclass.initEvents.call(this);
47919  
47920     },
47921
47922     onDestroy : function(){
47923        
47924         if(this.store){
47925             this.store.un('beforeload', this.onBeforeLoad, this);
47926             this.store.un('load', this.onLoad, this);
47927             this.store.un('loadexception', this.onLoadException, this);
47928         }
47929         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47930     },
47931
47932     // private
47933     fireKey : function(e){
47934         if(e.isNavKeyPress() && !this.list.isVisible()){
47935             this.fireEvent("specialkey", this, e);
47936         }
47937     },
47938
47939     // private
47940     onResize: function(w, h){
47941         
47942         return; 
47943     
47944         
47945     },
47946
47947     /**
47948      * Allow or prevent the user from directly editing the field text.  If false is passed,
47949      * the user will only be able to select from the items defined in the dropdown list.  This method
47950      * is the runtime equivalent of setting the 'editable' config option at config time.
47951      * @param {Boolean} value True to allow the user to directly edit the field text
47952      */
47953     setEditable : function(value){
47954          
47955     },
47956
47957     // private
47958     onBeforeLoad : function(){
47959         
47960         Roo.log("Select before load");
47961         return;
47962     
47963         this.innerList.update(this.loadingText ?
47964                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47965         //this.restrictHeight();
47966         this.selectedIndex = -1;
47967     },
47968
47969     // private
47970     onLoad : function(){
47971
47972     
47973         var dom = this.el.dom;
47974         dom.innerHTML = '';
47975          var od = dom.ownerDocument;
47976          
47977         if (this.emptyText) {
47978             var op = od.createElement('option');
47979             op.setAttribute('value', '');
47980             op.innerHTML = String.format('{0}', this.emptyText);
47981             dom.appendChild(op);
47982         }
47983         if(this.store.getCount() > 0){
47984            
47985             var vf = this.valueField;
47986             var df = this.displayField;
47987             this.store.data.each(function(r) {
47988                 // which colmsn to use... testing - cdoe / title..
47989                 var op = od.createElement('option');
47990                 op.setAttribute('value', r.data[vf]);
47991                 op.innerHTML = String.format('{0}', r.data[df]);
47992                 dom.appendChild(op);
47993             });
47994             if (typeof(this.defaultValue != 'undefined')) {
47995                 this.setValue(this.defaultValue);
47996             }
47997             
47998              
47999         }else{
48000             //this.onEmptyResults();
48001         }
48002         //this.el.focus();
48003     },
48004     // private
48005     onLoadException : function()
48006     {
48007         dom.innerHTML = '';
48008             
48009         Roo.log("Select on load exception");
48010         return;
48011     
48012         this.collapse();
48013         Roo.log(this.store.reader.jsonData);
48014         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48015             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48016         }
48017         
48018         
48019     },
48020     // private
48021     onTypeAhead : function(){
48022          
48023     },
48024
48025     // private
48026     onSelect : function(record, index){
48027         Roo.log('on select?');
48028         return;
48029         if(this.fireEvent('beforeselect', this, record, index) !== false){
48030             this.setFromData(index > -1 ? record.data : false);
48031             this.collapse();
48032             this.fireEvent('select', this, record, index);
48033         }
48034     },
48035
48036     /**
48037      * Returns the currently selected field value or empty string if no value is set.
48038      * @return {String} value The selected value
48039      */
48040     getValue : function(){
48041         var dom = this.el.dom;
48042         this.value = dom.options[dom.selectedIndex].value;
48043         return this.value;
48044         
48045     },
48046
48047     /**
48048      * Clears any text/value currently set in the field
48049      */
48050     clearValue : function(){
48051         this.value = '';
48052         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48053         
48054     },
48055
48056     /**
48057      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48058      * will be displayed in the field.  If the value does not match the data value of an existing item,
48059      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48060      * Otherwise the field will be blank (although the value will still be set).
48061      * @param {String} value The value to match
48062      */
48063     setValue : function(v){
48064         var d = this.el.dom;
48065         for (var i =0; i < d.options.length;i++) {
48066             if (v == d.options[i].value) {
48067                 d.selectedIndex = i;
48068                 this.value = v;
48069                 return;
48070             }
48071         }
48072         this.clearValue();
48073     },
48074     /**
48075      * @property {Object} the last set data for the element
48076      */
48077     
48078     lastData : false,
48079     /**
48080      * Sets the value of the field based on a object which is related to the record format for the store.
48081      * @param {Object} value the value to set as. or false on reset?
48082      */
48083     setFromData : function(o){
48084         Roo.log('setfrom data?');
48085          
48086         
48087         
48088     },
48089     // private
48090     reset : function(){
48091         this.clearValue();
48092     },
48093     // private
48094     findRecord : function(prop, value){
48095         
48096         return false;
48097     
48098         var record;
48099         if(this.store.getCount() > 0){
48100             this.store.each(function(r){
48101                 if(r.data[prop] == value){
48102                     record = r;
48103                     return false;
48104                 }
48105                 return true;
48106             });
48107         }
48108         return record;
48109     },
48110     
48111     getName: function()
48112     {
48113         // returns hidden if it's set..
48114         if (!this.rendered) {return ''};
48115         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48116         
48117     },
48118      
48119
48120     
48121
48122     // private
48123     onEmptyResults : function(){
48124         Roo.log('empty results');
48125         //this.collapse();
48126     },
48127
48128     /**
48129      * Returns true if the dropdown list is expanded, else false.
48130      */
48131     isExpanded : function(){
48132         return false;
48133     },
48134
48135     /**
48136      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48137      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48138      * @param {String} value The data value of the item to select
48139      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48140      * selected item if it is not currently in view (defaults to true)
48141      * @return {Boolean} True if the value matched an item in the list, else false
48142      */
48143     selectByValue : function(v, scrollIntoView){
48144         Roo.log('select By Value');
48145         return false;
48146     
48147         if(v !== undefined && v !== null){
48148             var r = this.findRecord(this.valueField || this.displayField, v);
48149             if(r){
48150                 this.select(this.store.indexOf(r), scrollIntoView);
48151                 return true;
48152             }
48153         }
48154         return false;
48155     },
48156
48157     /**
48158      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48159      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48160      * @param {Number} index The zero-based index of the list item to select
48161      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48162      * selected item if it is not currently in view (defaults to true)
48163      */
48164     select : function(index, scrollIntoView){
48165         Roo.log('select ');
48166         return  ;
48167         
48168         this.selectedIndex = index;
48169         this.view.select(index);
48170         if(scrollIntoView !== false){
48171             var el = this.view.getNode(index);
48172             if(el){
48173                 this.innerList.scrollChildIntoView(el, false);
48174             }
48175         }
48176     },
48177
48178       
48179
48180     // private
48181     validateBlur : function(){
48182         
48183         return;
48184         
48185     },
48186
48187     // private
48188     initQuery : function(){
48189         this.doQuery(this.getRawValue());
48190     },
48191
48192     // private
48193     doForce : function(){
48194         if(this.el.dom.value.length > 0){
48195             this.el.dom.value =
48196                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48197              
48198         }
48199     },
48200
48201     /**
48202      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48203      * query allowing the query action to be canceled if needed.
48204      * @param {String} query The SQL query to execute
48205      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48206      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48207      * saved in the current store (defaults to false)
48208      */
48209     doQuery : function(q, forceAll){
48210         
48211         Roo.log('doQuery?');
48212         if(q === undefined || q === null){
48213             q = '';
48214         }
48215         var qe = {
48216             query: q,
48217             forceAll: forceAll,
48218             combo: this,
48219             cancel:false
48220         };
48221         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48222             return false;
48223         }
48224         q = qe.query;
48225         forceAll = qe.forceAll;
48226         if(forceAll === true || (q.length >= this.minChars)){
48227             if(this.lastQuery != q || this.alwaysQuery){
48228                 this.lastQuery = q;
48229                 if(this.mode == 'local'){
48230                     this.selectedIndex = -1;
48231                     if(forceAll){
48232                         this.store.clearFilter();
48233                     }else{
48234                         this.store.filter(this.displayField, q);
48235                     }
48236                     this.onLoad();
48237                 }else{
48238                     this.store.baseParams[this.queryParam] = q;
48239                     this.store.load({
48240                         params: this.getParams(q)
48241                     });
48242                     this.expand();
48243                 }
48244             }else{
48245                 this.selectedIndex = -1;
48246                 this.onLoad();   
48247             }
48248         }
48249     },
48250
48251     // private
48252     getParams : function(q){
48253         var p = {};
48254         //p[this.queryParam] = q;
48255         if(this.pageSize){
48256             p.start = 0;
48257             p.limit = this.pageSize;
48258         }
48259         return p;
48260     },
48261
48262     /**
48263      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48264      */
48265     collapse : function(){
48266         
48267     },
48268
48269     // private
48270     collapseIf : function(e){
48271         
48272     },
48273
48274     /**
48275      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48276      */
48277     expand : function(){
48278         
48279     } ,
48280
48281     // private
48282      
48283
48284     /** 
48285     * @cfg {Boolean} grow 
48286     * @hide 
48287     */
48288     /** 
48289     * @cfg {Number} growMin 
48290     * @hide 
48291     */
48292     /** 
48293     * @cfg {Number} growMax 
48294     * @hide 
48295     */
48296     /**
48297      * @hide
48298      * @method autoSize
48299      */
48300     
48301     setWidth : function()
48302     {
48303         
48304     },
48305     getResizeEl : function(){
48306         return this.el;
48307     }
48308 });//<script type="text/javasscript">
48309  
48310
48311 /**
48312  * @class Roo.DDView
48313  * A DnD enabled version of Roo.View.
48314  * @param {Element/String} container The Element in which to create the View.
48315  * @param {String} tpl The template string used to create the markup for each element of the View
48316  * @param {Object} config The configuration properties. These include all the config options of
48317  * {@link Roo.View} plus some specific to this class.<br>
48318  * <p>
48319  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48320  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48321  * <p>
48322  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48323 .x-view-drag-insert-above {
48324         border-top:1px dotted #3366cc;
48325 }
48326 .x-view-drag-insert-below {
48327         border-bottom:1px dotted #3366cc;
48328 }
48329 </code></pre>
48330  * 
48331  */
48332  
48333 Roo.DDView = function(container, tpl, config) {
48334     Roo.DDView.superclass.constructor.apply(this, arguments);
48335     this.getEl().setStyle("outline", "0px none");
48336     this.getEl().unselectable();
48337     if (this.dragGroup) {
48338                 this.setDraggable(this.dragGroup.split(","));
48339     }
48340     if (this.dropGroup) {
48341                 this.setDroppable(this.dropGroup.split(","));
48342     }
48343     if (this.deletable) {
48344         this.setDeletable();
48345     }
48346     this.isDirtyFlag = false;
48347         this.addEvents({
48348                 "drop" : true
48349         });
48350 };
48351
48352 Roo.extend(Roo.DDView, Roo.View, {
48353 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48354 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48355 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48356 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48357
48358         isFormField: true,
48359
48360         reset: Roo.emptyFn,
48361         
48362         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48363
48364         validate: function() {
48365                 return true;
48366         },
48367         
48368         destroy: function() {
48369                 this.purgeListeners();
48370                 this.getEl.removeAllListeners();
48371                 this.getEl().remove();
48372                 if (this.dragZone) {
48373                         if (this.dragZone.destroy) {
48374                                 this.dragZone.destroy();
48375                         }
48376                 }
48377                 if (this.dropZone) {
48378                         if (this.dropZone.destroy) {
48379                                 this.dropZone.destroy();
48380                         }
48381                 }
48382         },
48383
48384 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48385         getName: function() {
48386                 return this.name;
48387         },
48388
48389 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48390         setValue: function(v) {
48391                 if (!this.store) {
48392                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48393                 }
48394                 var data = {};
48395                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48396                 this.store.proxy = new Roo.data.MemoryProxy(data);
48397                 this.store.load();
48398         },
48399
48400 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48401         getValue: function() {
48402                 var result = '(';
48403                 this.store.each(function(rec) {
48404                         result += rec.id + ',';
48405                 });
48406                 return result.substr(0, result.length - 1) + ')';
48407         },
48408         
48409         getIds: function() {
48410                 var i = 0, result = new Array(this.store.getCount());
48411                 this.store.each(function(rec) {
48412                         result[i++] = rec.id;
48413                 });
48414                 return result;
48415         },
48416         
48417         isDirty: function() {
48418                 return this.isDirtyFlag;
48419         },
48420
48421 /**
48422  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48423  *      whole Element becomes the target, and this causes the drop gesture to append.
48424  */
48425     getTargetFromEvent : function(e) {
48426                 var target = e.getTarget();
48427                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48428                 target = target.parentNode;
48429                 }
48430                 if (!target) {
48431                         target = this.el.dom.lastChild || this.el.dom;
48432                 }
48433                 return target;
48434     },
48435
48436 /**
48437  *      Create the drag data which consists of an object which has the property "ddel" as
48438  *      the drag proxy element. 
48439  */
48440     getDragData : function(e) {
48441         var target = this.findItemFromChild(e.getTarget());
48442                 if(target) {
48443                         this.handleSelection(e);
48444                         var selNodes = this.getSelectedNodes();
48445             var dragData = {
48446                 source: this,
48447                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48448                 nodes: selNodes,
48449                 records: []
48450                         };
48451                         var selectedIndices = this.getSelectedIndexes();
48452                         for (var i = 0; i < selectedIndices.length; i++) {
48453                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48454                         }
48455                         if (selNodes.length == 1) {
48456                                 dragData.ddel = target.cloneNode(true); // the div element
48457                         } else {
48458                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48459                                 div.className = 'multi-proxy';
48460                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48461                                         div.appendChild(selNodes[i].cloneNode(true));
48462                                 }
48463                                 dragData.ddel = div;
48464                         }
48465             //console.log(dragData)
48466             //console.log(dragData.ddel.innerHTML)
48467                         return dragData;
48468                 }
48469         //console.log('nodragData')
48470                 return false;
48471     },
48472     
48473 /**     Specify to which ddGroup items in this DDView may be dragged. */
48474     setDraggable: function(ddGroup) {
48475         if (ddGroup instanceof Array) {
48476                 Roo.each(ddGroup, this.setDraggable, this);
48477                 return;
48478         }
48479         if (this.dragZone) {
48480                 this.dragZone.addToGroup(ddGroup);
48481         } else {
48482                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48483                                 containerScroll: true,
48484                                 ddGroup: ddGroup 
48485
48486                         });
48487 //                      Draggability implies selection. DragZone's mousedown selects the element.
48488                         if (!this.multiSelect) { this.singleSelect = true; }
48489
48490 //                      Wire the DragZone's handlers up to methods in *this*
48491                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48492                 }
48493     },
48494
48495 /**     Specify from which ddGroup this DDView accepts drops. */
48496     setDroppable: function(ddGroup) {
48497         if (ddGroup instanceof Array) {
48498                 Roo.each(ddGroup, this.setDroppable, this);
48499                 return;
48500         }
48501         if (this.dropZone) {
48502                 this.dropZone.addToGroup(ddGroup);
48503         } else {
48504                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48505                                 containerScroll: true,
48506                                 ddGroup: ddGroup
48507                         });
48508
48509 //                      Wire the DropZone's handlers up to methods in *this*
48510                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48511                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48512                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48513                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48514                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48515                 }
48516     },
48517
48518 /**     Decide whether to drop above or below a View node. */
48519     getDropPoint : function(e, n, dd){
48520         if (n == this.el.dom) { return "above"; }
48521                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48522                 var c = t + (b - t) / 2;
48523                 var y = Roo.lib.Event.getPageY(e);
48524                 if(y <= c) {
48525                         return "above";
48526                 }else{
48527                         return "below";
48528                 }
48529     },
48530
48531     onNodeEnter : function(n, dd, e, data){
48532                 return false;
48533     },
48534     
48535     onNodeOver : function(n, dd, e, data){
48536                 var pt = this.getDropPoint(e, n, dd);
48537                 // set the insert point style on the target node
48538                 var dragElClass = this.dropNotAllowed;
48539                 if (pt) {
48540                         var targetElClass;
48541                         if (pt == "above"){
48542                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48543                                 targetElClass = "x-view-drag-insert-above";
48544                         } else {
48545                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48546                                 targetElClass = "x-view-drag-insert-below";
48547                         }
48548                         if (this.lastInsertClass != targetElClass){
48549                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48550                                 this.lastInsertClass = targetElClass;
48551                         }
48552                 }
48553                 return dragElClass;
48554         },
48555
48556     onNodeOut : function(n, dd, e, data){
48557                 this.removeDropIndicators(n);
48558     },
48559
48560     onNodeDrop : function(n, dd, e, data){
48561         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48562                 return false;
48563         }
48564         var pt = this.getDropPoint(e, n, dd);
48565                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48566                 if (pt == "below") { insertAt++; }
48567                 for (var i = 0; i < data.records.length; i++) {
48568                         var r = data.records[i];
48569                         var dup = this.store.getById(r.id);
48570                         if (dup && (dd != this.dragZone)) {
48571                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48572                         } else {
48573                                 if (data.copy) {
48574                                         this.store.insert(insertAt++, r.copy());
48575                                 } else {
48576                                         data.source.isDirtyFlag = true;
48577                                         r.store.remove(r);
48578                                         this.store.insert(insertAt++, r);
48579                                 }
48580                                 this.isDirtyFlag = true;
48581                         }
48582                 }
48583                 this.dragZone.cachedTarget = null;
48584                 return true;
48585     },
48586
48587     removeDropIndicators : function(n){
48588                 if(n){
48589                         Roo.fly(n).removeClass([
48590                                 "x-view-drag-insert-above",
48591                                 "x-view-drag-insert-below"]);
48592                         this.lastInsertClass = "_noclass";
48593                 }
48594     },
48595
48596 /**
48597  *      Utility method. Add a delete option to the DDView's context menu.
48598  *      @param {String} imageUrl The URL of the "delete" icon image.
48599  */
48600         setDeletable: function(imageUrl) {
48601                 if (!this.singleSelect && !this.multiSelect) {
48602                         this.singleSelect = true;
48603                 }
48604                 var c = this.getContextMenu();
48605                 this.contextMenu.on("itemclick", function(item) {
48606                         switch (item.id) {
48607                                 case "delete":
48608                                         this.remove(this.getSelectedIndexes());
48609                                         break;
48610                         }
48611                 }, this);
48612                 this.contextMenu.add({
48613                         icon: imageUrl,
48614                         id: "delete",
48615                         text: 'Delete'
48616                 });
48617         },
48618         
48619 /**     Return the context menu for this DDView. */
48620         getContextMenu: function() {
48621                 if (!this.contextMenu) {
48622 //                      Create the View's context menu
48623                         this.contextMenu = new Roo.menu.Menu({
48624                                 id: this.id + "-contextmenu"
48625                         });
48626                         this.el.on("contextmenu", this.showContextMenu, this);
48627                 }
48628                 return this.contextMenu;
48629         },
48630         
48631         disableContextMenu: function() {
48632                 if (this.contextMenu) {
48633                         this.el.un("contextmenu", this.showContextMenu, this);
48634                 }
48635         },
48636
48637         showContextMenu: function(e, item) {
48638         item = this.findItemFromChild(e.getTarget());
48639                 if (item) {
48640                         e.stopEvent();
48641                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48642                         this.contextMenu.showAt(e.getXY());
48643             }
48644     },
48645
48646 /**
48647  *      Remove {@link Roo.data.Record}s at the specified indices.
48648  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48649  */
48650     remove: function(selectedIndices) {
48651                 selectedIndices = [].concat(selectedIndices);
48652                 for (var i = 0; i < selectedIndices.length; i++) {
48653                         var rec = this.store.getAt(selectedIndices[i]);
48654                         this.store.remove(rec);
48655                 }
48656     },
48657
48658 /**
48659  *      Double click fires the event, but also, if this is draggable, and there is only one other
48660  *      related DropZone, it transfers the selected node.
48661  */
48662     onDblClick : function(e){
48663         var item = this.findItemFromChild(e.getTarget());
48664         if(item){
48665             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48666                 return false;
48667             }
48668             if (this.dragGroup) {
48669                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48670                     while (targets.indexOf(this.dropZone) > -1) {
48671                             targets.remove(this.dropZone);
48672                                 }
48673                     if (targets.length == 1) {
48674                                         this.dragZone.cachedTarget = null;
48675                         var el = Roo.get(targets[0].getEl());
48676                         var box = el.getBox(true);
48677                         targets[0].onNodeDrop(el.dom, {
48678                                 target: el.dom,
48679                                 xy: [box.x, box.y + box.height - 1]
48680                         }, null, this.getDragData(e));
48681                     }
48682                 }
48683         }
48684     },
48685     
48686     handleSelection: function(e) {
48687                 this.dragZone.cachedTarget = null;
48688         var item = this.findItemFromChild(e.getTarget());
48689         if (!item) {
48690                 this.clearSelections(true);
48691                 return;
48692         }
48693                 if (item && (this.multiSelect || this.singleSelect)){
48694                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48695                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48696                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48697                                 this.unselect(item);
48698                         } else {
48699                                 this.select(item, this.multiSelect && e.ctrlKey);
48700                                 this.lastSelection = item;
48701                         }
48702                 }
48703     },
48704
48705     onItemClick : function(item, index, e){
48706                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48707                         return false;
48708                 }
48709                 return true;
48710     },
48711
48712     unselect : function(nodeInfo, suppressEvent){
48713                 var node = this.getNode(nodeInfo);
48714                 if(node && this.isSelected(node)){
48715                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48716                                 Roo.fly(node).removeClass(this.selectedClass);
48717                                 this.selections.remove(node);
48718                                 if(!suppressEvent){
48719                                         this.fireEvent("selectionchange", this, this.selections);
48720                                 }
48721                         }
48722                 }
48723     }
48724 });
48725 /*
48726  * Based on:
48727  * Ext JS Library 1.1.1
48728  * Copyright(c) 2006-2007, Ext JS, LLC.
48729  *
48730  * Originally Released Under LGPL - original licence link has changed is not relivant.
48731  *
48732  * Fork - LGPL
48733  * <script type="text/javascript">
48734  */
48735  
48736 /**
48737  * @class Roo.LayoutManager
48738  * @extends Roo.util.Observable
48739  * Base class for layout managers.
48740  */
48741 Roo.LayoutManager = function(container, config){
48742     Roo.LayoutManager.superclass.constructor.call(this);
48743     this.el = Roo.get(container);
48744     // ie scrollbar fix
48745     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48746         document.body.scroll = "no";
48747     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48748         this.el.position('relative');
48749     }
48750     this.id = this.el.id;
48751     this.el.addClass("x-layout-container");
48752     /** false to disable window resize monitoring @type Boolean */
48753     this.monitorWindowResize = true;
48754     this.regions = {};
48755     this.addEvents({
48756         /**
48757          * @event layout
48758          * Fires when a layout is performed. 
48759          * @param {Roo.LayoutManager} this
48760          */
48761         "layout" : true,
48762         /**
48763          * @event regionresized
48764          * Fires when the user resizes a region. 
48765          * @param {Roo.LayoutRegion} region The resized region
48766          * @param {Number} newSize The new size (width for east/west, height for north/south)
48767          */
48768         "regionresized" : true,
48769         /**
48770          * @event regioncollapsed
48771          * Fires when a region is collapsed. 
48772          * @param {Roo.LayoutRegion} region The collapsed region
48773          */
48774         "regioncollapsed" : true,
48775         /**
48776          * @event regionexpanded
48777          * Fires when a region is expanded.  
48778          * @param {Roo.LayoutRegion} region The expanded region
48779          */
48780         "regionexpanded" : true
48781     });
48782     this.updating = false;
48783     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48784 };
48785
48786 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48787     /**
48788      * Returns true if this layout is currently being updated
48789      * @return {Boolean}
48790      */
48791     isUpdating : function(){
48792         return this.updating; 
48793     },
48794     
48795     /**
48796      * Suspend the LayoutManager from doing auto-layouts while
48797      * making multiple add or remove calls
48798      */
48799     beginUpdate : function(){
48800         this.updating = true;    
48801     },
48802     
48803     /**
48804      * Restore auto-layouts and optionally disable the manager from performing a layout
48805      * @param {Boolean} noLayout true to disable a layout update 
48806      */
48807     endUpdate : function(noLayout){
48808         this.updating = false;
48809         if(!noLayout){
48810             this.layout();
48811         }    
48812     },
48813     
48814     layout: function(){
48815         
48816     },
48817     
48818     onRegionResized : function(region, newSize){
48819         this.fireEvent("regionresized", region, newSize);
48820         this.layout();
48821     },
48822     
48823     onRegionCollapsed : function(region){
48824         this.fireEvent("regioncollapsed", region);
48825     },
48826     
48827     onRegionExpanded : function(region){
48828         this.fireEvent("regionexpanded", region);
48829     },
48830         
48831     /**
48832      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48833      * performs box-model adjustments.
48834      * @return {Object} The size as an object {width: (the width), height: (the height)}
48835      */
48836     getViewSize : function(){
48837         var size;
48838         if(this.el.dom != document.body){
48839             size = this.el.getSize();
48840         }else{
48841             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48842         }
48843         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48844         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48845         return size;
48846     },
48847     
48848     /**
48849      * Returns the Element this layout is bound to.
48850      * @return {Roo.Element}
48851      */
48852     getEl : function(){
48853         return this.el;
48854     },
48855     
48856     /**
48857      * Returns the specified region.
48858      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48859      * @return {Roo.LayoutRegion}
48860      */
48861     getRegion : function(target){
48862         return this.regions[target.toLowerCase()];
48863     },
48864     
48865     onWindowResize : function(){
48866         if(this.monitorWindowResize){
48867             this.layout();
48868         }
48869     }
48870 });/*
48871  * Based on:
48872  * Ext JS Library 1.1.1
48873  * Copyright(c) 2006-2007, Ext JS, LLC.
48874  *
48875  * Originally Released Under LGPL - original licence link has changed is not relivant.
48876  *
48877  * Fork - LGPL
48878  * <script type="text/javascript">
48879  */
48880 /**
48881  * @class Roo.BorderLayout
48882  * @extends Roo.LayoutManager
48883  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48884  * please see: <br><br>
48885  * <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>
48886  * <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>
48887  * Example:
48888  <pre><code>
48889  var layout = new Roo.BorderLayout(document.body, {
48890     north: {
48891         initialSize: 25,
48892         titlebar: false
48893     },
48894     west: {
48895         split:true,
48896         initialSize: 200,
48897         minSize: 175,
48898         maxSize: 400,
48899         titlebar: true,
48900         collapsible: true
48901     },
48902     east: {
48903         split:true,
48904         initialSize: 202,
48905         minSize: 175,
48906         maxSize: 400,
48907         titlebar: true,
48908         collapsible: true
48909     },
48910     south: {
48911         split:true,
48912         initialSize: 100,
48913         minSize: 100,
48914         maxSize: 200,
48915         titlebar: true,
48916         collapsible: true
48917     },
48918     center: {
48919         titlebar: true,
48920         autoScroll:true,
48921         resizeTabs: true,
48922         minTabWidth: 50,
48923         preferredTabWidth: 150
48924     }
48925 });
48926
48927 // shorthand
48928 var CP = Roo.ContentPanel;
48929
48930 layout.beginUpdate();
48931 layout.add("north", new CP("north", "North"));
48932 layout.add("south", new CP("south", {title: "South", closable: true}));
48933 layout.add("west", new CP("west", {title: "West"}));
48934 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48935 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48936 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48937 layout.getRegion("center").showPanel("center1");
48938 layout.endUpdate();
48939 </code></pre>
48940
48941 <b>The container the layout is rendered into can be either the body element or any other element.
48942 If it is not the body element, the container needs to either be an absolute positioned element,
48943 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48944 the container size if it is not the body element.</b>
48945
48946 * @constructor
48947 * Create a new BorderLayout
48948 * @param {String/HTMLElement/Element} container The container this layout is bound to
48949 * @param {Object} config Configuration options
48950  */
48951 Roo.BorderLayout = function(container, config){
48952     config = config || {};
48953     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48954     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48955     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48956         var target = this.factory.validRegions[i];
48957         if(config[target]){
48958             this.addRegion(target, config[target]);
48959         }
48960     }
48961 };
48962
48963 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48964     /**
48965      * Creates and adds a new region if it doesn't already exist.
48966      * @param {String} target The target region key (north, south, east, west or center).
48967      * @param {Object} config The regions config object
48968      * @return {BorderLayoutRegion} The new region
48969      */
48970     addRegion : function(target, config){
48971         if(!this.regions[target]){
48972             var r = this.factory.create(target, this, config);
48973             this.bindRegion(target, r);
48974         }
48975         return this.regions[target];
48976     },
48977
48978     // private (kinda)
48979     bindRegion : function(name, r){
48980         this.regions[name] = r;
48981         r.on("visibilitychange", this.layout, this);
48982         r.on("paneladded", this.layout, this);
48983         r.on("panelremoved", this.layout, this);
48984         r.on("invalidated", this.layout, this);
48985         r.on("resized", this.onRegionResized, this);
48986         r.on("collapsed", this.onRegionCollapsed, this);
48987         r.on("expanded", this.onRegionExpanded, this);
48988     },
48989
48990     /**
48991      * Performs a layout update.
48992      */
48993     layout : function(){
48994         if(this.updating) return;
48995         var size = this.getViewSize();
48996         var w = size.width;
48997         var h = size.height;
48998         var centerW = w;
48999         var centerH = h;
49000         var centerY = 0;
49001         var centerX = 0;
49002         //var x = 0, y = 0;
49003
49004         var rs = this.regions;
49005         var north = rs["north"];
49006         var south = rs["south"]; 
49007         var west = rs["west"];
49008         var east = rs["east"];
49009         var center = rs["center"];
49010         //if(this.hideOnLayout){ // not supported anymore
49011             //c.el.setStyle("display", "none");
49012         //}
49013         if(north && north.isVisible()){
49014             var b = north.getBox();
49015             var m = north.getMargins();
49016             b.width = w - (m.left+m.right);
49017             b.x = m.left;
49018             b.y = m.top;
49019             centerY = b.height + b.y + m.bottom;
49020             centerH -= centerY;
49021             north.updateBox(this.safeBox(b));
49022         }
49023         if(south && south.isVisible()){
49024             var b = south.getBox();
49025             var m = south.getMargins();
49026             b.width = w - (m.left+m.right);
49027             b.x = m.left;
49028             var totalHeight = (b.height + m.top + m.bottom);
49029             b.y = h - totalHeight + m.top;
49030             centerH -= totalHeight;
49031             south.updateBox(this.safeBox(b));
49032         }
49033         if(west && west.isVisible()){
49034             var b = west.getBox();
49035             var m = west.getMargins();
49036             b.height = centerH - (m.top+m.bottom);
49037             b.x = m.left;
49038             b.y = centerY + m.top;
49039             var totalWidth = (b.width + m.left + m.right);
49040             centerX += totalWidth;
49041             centerW -= totalWidth;
49042             west.updateBox(this.safeBox(b));
49043         }
49044         if(east && east.isVisible()){
49045             var b = east.getBox();
49046             var m = east.getMargins();
49047             b.height = centerH - (m.top+m.bottom);
49048             var totalWidth = (b.width + m.left + m.right);
49049             b.x = w - totalWidth + m.left;
49050             b.y = centerY + m.top;
49051             centerW -= totalWidth;
49052             east.updateBox(this.safeBox(b));
49053         }
49054         if(center){
49055             var m = center.getMargins();
49056             var centerBox = {
49057                 x: centerX + m.left,
49058                 y: centerY + m.top,
49059                 width: centerW - (m.left+m.right),
49060                 height: centerH - (m.top+m.bottom)
49061             };
49062             //if(this.hideOnLayout){
49063                 //center.el.setStyle("display", "block");
49064             //}
49065             center.updateBox(this.safeBox(centerBox));
49066         }
49067         this.el.repaint();
49068         this.fireEvent("layout", this);
49069     },
49070
49071     // private
49072     safeBox : function(box){
49073         box.width = Math.max(0, box.width);
49074         box.height = Math.max(0, box.height);
49075         return box;
49076     },
49077
49078     /**
49079      * Adds a ContentPanel (or subclass) to this layout.
49080      * @param {String} target The target region key (north, south, east, west or center).
49081      * @param {Roo.ContentPanel} panel The panel to add
49082      * @return {Roo.ContentPanel} The added panel
49083      */
49084     add : function(target, panel){
49085          
49086         target = target.toLowerCase();
49087         return this.regions[target].add(panel);
49088     },
49089
49090     /**
49091      * Remove a ContentPanel (or subclass) to this layout.
49092      * @param {String} target The target region key (north, south, east, west or center).
49093      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49094      * @return {Roo.ContentPanel} The removed panel
49095      */
49096     remove : function(target, panel){
49097         target = target.toLowerCase();
49098         return this.regions[target].remove(panel);
49099     },
49100
49101     /**
49102      * Searches all regions for a panel with the specified id
49103      * @param {String} panelId
49104      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49105      */
49106     findPanel : function(panelId){
49107         var rs = this.regions;
49108         for(var target in rs){
49109             if(typeof rs[target] != "function"){
49110                 var p = rs[target].getPanel(panelId);
49111                 if(p){
49112                     return p;
49113                 }
49114             }
49115         }
49116         return null;
49117     },
49118
49119     /**
49120      * Searches all regions for a panel with the specified id and activates (shows) it.
49121      * @param {String/ContentPanel} panelId The panels id or the panel itself
49122      * @return {Roo.ContentPanel} The shown panel or null
49123      */
49124     showPanel : function(panelId) {
49125       var rs = this.regions;
49126       for(var target in rs){
49127          var r = rs[target];
49128          if(typeof r != "function"){
49129             if(r.hasPanel(panelId)){
49130                return r.showPanel(panelId);
49131             }
49132          }
49133       }
49134       return null;
49135    },
49136
49137    /**
49138      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49139      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49140      */
49141     restoreState : function(provider){
49142         if(!provider){
49143             provider = Roo.state.Manager;
49144         }
49145         var sm = new Roo.LayoutStateManager();
49146         sm.init(this, provider);
49147     },
49148
49149     /**
49150      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49151      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49152      * a valid ContentPanel config object.  Example:
49153      * <pre><code>
49154 // Create the main layout
49155 var layout = new Roo.BorderLayout('main-ct', {
49156     west: {
49157         split:true,
49158         minSize: 175,
49159         titlebar: true
49160     },
49161     center: {
49162         title:'Components'
49163     }
49164 }, 'main-ct');
49165
49166 // Create and add multiple ContentPanels at once via configs
49167 layout.batchAdd({
49168    west: {
49169        id: 'source-files',
49170        autoCreate:true,
49171        title:'Ext Source Files',
49172        autoScroll:true,
49173        fitToFrame:true
49174    },
49175    center : {
49176        el: cview,
49177        autoScroll:true,
49178        fitToFrame:true,
49179        toolbar: tb,
49180        resizeEl:'cbody'
49181    }
49182 });
49183 </code></pre>
49184      * @param {Object} regions An object containing ContentPanel configs by region name
49185      */
49186     batchAdd : function(regions){
49187         this.beginUpdate();
49188         for(var rname in regions){
49189             var lr = this.regions[rname];
49190             if(lr){
49191                 this.addTypedPanels(lr, regions[rname]);
49192             }
49193         }
49194         this.endUpdate();
49195     },
49196
49197     // private
49198     addTypedPanels : function(lr, ps){
49199         if(typeof ps == 'string'){
49200             lr.add(new Roo.ContentPanel(ps));
49201         }
49202         else if(ps instanceof Array){
49203             for(var i =0, len = ps.length; i < len; i++){
49204                 this.addTypedPanels(lr, ps[i]);
49205             }
49206         }
49207         else if(!ps.events){ // raw config?
49208             var el = ps.el;
49209             delete ps.el; // prevent conflict
49210             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49211         }
49212         else {  // panel object assumed!
49213             lr.add(ps);
49214         }
49215     },
49216     /**
49217      * Adds a xtype elements to the layout.
49218      * <pre><code>
49219
49220 layout.addxtype({
49221        xtype : 'ContentPanel',
49222        region: 'west',
49223        items: [ .... ]
49224    }
49225 );
49226
49227 layout.addxtype({
49228         xtype : 'NestedLayoutPanel',
49229         region: 'west',
49230         layout: {
49231            center: { },
49232            west: { }   
49233         },
49234         items : [ ... list of content panels or nested layout panels.. ]
49235    }
49236 );
49237 </code></pre>
49238      * @param {Object} cfg Xtype definition of item to add.
49239      */
49240     addxtype : function(cfg)
49241     {
49242         // basically accepts a pannel...
49243         // can accept a layout region..!?!?
49244         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49245         
49246         if (!cfg.xtype.match(/Panel$/)) {
49247             return false;
49248         }
49249         var ret = false;
49250         
49251         if (typeof(cfg.region) == 'undefined') {
49252             Roo.log("Failed to add Panel, region was not set");
49253             Roo.log(cfg);
49254             return false;
49255         }
49256         var region = cfg.region;
49257         delete cfg.region;
49258         
49259           
49260         var xitems = [];
49261         if (cfg.items) {
49262             xitems = cfg.items;
49263             delete cfg.items;
49264         }
49265         var nb = false;
49266         
49267         switch(cfg.xtype) 
49268         {
49269             case 'ContentPanel':  // ContentPanel (el, cfg)
49270             case 'ScrollPanel':  // ContentPanel (el, cfg)
49271             case 'ViewPanel': 
49272                 if(cfg.autoCreate) {
49273                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49274                 } else {
49275                     var el = this.el.createChild();
49276                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49277                 }
49278                 
49279                 this.add(region, ret);
49280                 break;
49281             
49282             
49283             case 'TreePanel': // our new panel!
49284                 cfg.el = this.el.createChild();
49285                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49286                 this.add(region, ret);
49287                 break;
49288             
49289             case 'NestedLayoutPanel': 
49290                 // create a new Layout (which is  a Border Layout...
49291                 var el = this.el.createChild();
49292                 var clayout = cfg.layout;
49293                 delete cfg.layout;
49294                 clayout.items   = clayout.items  || [];
49295                 // replace this exitems with the clayout ones..
49296                 xitems = clayout.items;
49297                  
49298                 
49299                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49300                     cfg.background = false;
49301                 }
49302                 var layout = new Roo.BorderLayout(el, clayout);
49303                 
49304                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49305                 //console.log('adding nested layout panel '  + cfg.toSource());
49306                 this.add(region, ret);
49307                 nb = {}; /// find first...
49308                 break;
49309                 
49310             case 'GridPanel': 
49311             
49312                 // needs grid and region
49313                 
49314                 //var el = this.getRegion(region).el.createChild();
49315                 var el = this.el.createChild();
49316                 // create the grid first...
49317                 
49318                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49319                 delete cfg.grid;
49320                 if (region == 'center' && this.active ) {
49321                     cfg.background = false;
49322                 }
49323                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49324                 
49325                 this.add(region, ret);
49326                 if (cfg.background) {
49327                     ret.on('activate', function(gp) {
49328                         if (!gp.grid.rendered) {
49329                             gp.grid.render();
49330                         }
49331                     });
49332                 } else {
49333                     grid.render();
49334                 }
49335                 break;
49336            
49337            
49338            
49339                 
49340                 
49341                 
49342             default:
49343                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49344                     
49345                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49346                     this.add(region, ret);
49347                 } else {
49348                 
49349                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49350                     return null;
49351                 }
49352                 
49353              // GridPanel (grid, cfg)
49354             
49355         }
49356         this.beginUpdate();
49357         // add children..
49358         var region = '';
49359         var abn = {};
49360         Roo.each(xitems, function(i)  {
49361             region = nb && i.region ? i.region : false;
49362             
49363             var add = ret.addxtype(i);
49364            
49365             if (region) {
49366                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49367                 if (!i.background) {
49368                     abn[region] = nb[region] ;
49369                 }
49370             }
49371             
49372         });
49373         this.endUpdate();
49374
49375         // make the last non-background panel active..
49376         //if (nb) { Roo.log(abn); }
49377         if (nb) {
49378             
49379             for(var r in abn) {
49380                 region = this.getRegion(r);
49381                 if (region) {
49382                     // tried using nb[r], but it does not work..
49383                      
49384                     region.showPanel(abn[r]);
49385                    
49386                 }
49387             }
49388         }
49389         return ret;
49390         
49391     }
49392 });
49393
49394 /**
49395  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49396  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49397  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49398  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49399  * <pre><code>
49400 // shorthand
49401 var CP = Roo.ContentPanel;
49402
49403 var layout = Roo.BorderLayout.create({
49404     north: {
49405         initialSize: 25,
49406         titlebar: false,
49407         panels: [new CP("north", "North")]
49408     },
49409     west: {
49410         split:true,
49411         initialSize: 200,
49412         minSize: 175,
49413         maxSize: 400,
49414         titlebar: true,
49415         collapsible: true,
49416         panels: [new CP("west", {title: "West"})]
49417     },
49418     east: {
49419         split:true,
49420         initialSize: 202,
49421         minSize: 175,
49422         maxSize: 400,
49423         titlebar: true,
49424         collapsible: true,
49425         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49426     },
49427     south: {
49428         split:true,
49429         initialSize: 100,
49430         minSize: 100,
49431         maxSize: 200,
49432         titlebar: true,
49433         collapsible: true,
49434         panels: [new CP("south", {title: "South", closable: true})]
49435     },
49436     center: {
49437         titlebar: true,
49438         autoScroll:true,
49439         resizeTabs: true,
49440         minTabWidth: 50,
49441         preferredTabWidth: 150,
49442         panels: [
49443             new CP("center1", {title: "Close Me", closable: true}),
49444             new CP("center2", {title: "Center Panel", closable: false})
49445         ]
49446     }
49447 }, document.body);
49448
49449 layout.getRegion("center").showPanel("center1");
49450 </code></pre>
49451  * @param config
49452  * @param targetEl
49453  */
49454 Roo.BorderLayout.create = function(config, targetEl){
49455     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49456     layout.beginUpdate();
49457     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49458     for(var j = 0, jlen = regions.length; j < jlen; j++){
49459         var lr = regions[j];
49460         if(layout.regions[lr] && config[lr].panels){
49461             var r = layout.regions[lr];
49462             var ps = config[lr].panels;
49463             layout.addTypedPanels(r, ps);
49464         }
49465     }
49466     layout.endUpdate();
49467     return layout;
49468 };
49469
49470 // private
49471 Roo.BorderLayout.RegionFactory = {
49472     // private
49473     validRegions : ["north","south","east","west","center"],
49474
49475     // private
49476     create : function(target, mgr, config){
49477         target = target.toLowerCase();
49478         if(config.lightweight || config.basic){
49479             return new Roo.BasicLayoutRegion(mgr, config, target);
49480         }
49481         switch(target){
49482             case "north":
49483                 return new Roo.NorthLayoutRegion(mgr, config);
49484             case "south":
49485                 return new Roo.SouthLayoutRegion(mgr, config);
49486             case "east":
49487                 return new Roo.EastLayoutRegion(mgr, config);
49488             case "west":
49489                 return new Roo.WestLayoutRegion(mgr, config);
49490             case "center":
49491                 return new Roo.CenterLayoutRegion(mgr, config);
49492         }
49493         throw 'Layout region "'+target+'" not supported.';
49494     }
49495 };/*
49496  * Based on:
49497  * Ext JS Library 1.1.1
49498  * Copyright(c) 2006-2007, Ext JS, LLC.
49499  *
49500  * Originally Released Under LGPL - original licence link has changed is not relivant.
49501  *
49502  * Fork - LGPL
49503  * <script type="text/javascript">
49504  */
49505  
49506 /**
49507  * @class Roo.BasicLayoutRegion
49508  * @extends Roo.util.Observable
49509  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49510  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49511  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49512  */
49513 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49514     this.mgr = mgr;
49515     this.position  = pos;
49516     this.events = {
49517         /**
49518          * @scope Roo.BasicLayoutRegion
49519          */
49520         
49521         /**
49522          * @event beforeremove
49523          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49524          * @param {Roo.LayoutRegion} this
49525          * @param {Roo.ContentPanel} panel The panel
49526          * @param {Object} e The cancel event object
49527          */
49528         "beforeremove" : true,
49529         /**
49530          * @event invalidated
49531          * Fires when the layout for this region is changed.
49532          * @param {Roo.LayoutRegion} this
49533          */
49534         "invalidated" : true,
49535         /**
49536          * @event visibilitychange
49537          * Fires when this region is shown or hidden 
49538          * @param {Roo.LayoutRegion} this
49539          * @param {Boolean} visibility true or false
49540          */
49541         "visibilitychange" : true,
49542         /**
49543          * @event paneladded
49544          * Fires when a panel is added. 
49545          * @param {Roo.LayoutRegion} this
49546          * @param {Roo.ContentPanel} panel The panel
49547          */
49548         "paneladded" : true,
49549         /**
49550          * @event panelremoved
49551          * Fires when a panel is removed. 
49552          * @param {Roo.LayoutRegion} this
49553          * @param {Roo.ContentPanel} panel The panel
49554          */
49555         "panelremoved" : true,
49556         /**
49557          * @event collapsed
49558          * Fires when this region is collapsed.
49559          * @param {Roo.LayoutRegion} this
49560          */
49561         "collapsed" : true,
49562         /**
49563          * @event expanded
49564          * Fires when this region is expanded.
49565          * @param {Roo.LayoutRegion} this
49566          */
49567         "expanded" : true,
49568         /**
49569          * @event slideshow
49570          * Fires when this region is slid into view.
49571          * @param {Roo.LayoutRegion} this
49572          */
49573         "slideshow" : true,
49574         /**
49575          * @event slidehide
49576          * Fires when this region slides out of view. 
49577          * @param {Roo.LayoutRegion} this
49578          */
49579         "slidehide" : true,
49580         /**
49581          * @event panelactivated
49582          * Fires when a panel is activated. 
49583          * @param {Roo.LayoutRegion} this
49584          * @param {Roo.ContentPanel} panel The activated panel
49585          */
49586         "panelactivated" : true,
49587         /**
49588          * @event resized
49589          * Fires when the user resizes this region. 
49590          * @param {Roo.LayoutRegion} this
49591          * @param {Number} newSize The new size (width for east/west, height for north/south)
49592          */
49593         "resized" : true
49594     };
49595     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49596     this.panels = new Roo.util.MixedCollection();
49597     this.panels.getKey = this.getPanelId.createDelegate(this);
49598     this.box = null;
49599     this.activePanel = null;
49600     // ensure listeners are added...
49601     
49602     if (config.listeners || config.events) {
49603         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49604             listeners : config.listeners || {},
49605             events : config.events || {}
49606         });
49607     }
49608     
49609     if(skipConfig !== true){
49610         this.applyConfig(config);
49611     }
49612 };
49613
49614 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49615     getPanelId : function(p){
49616         return p.getId();
49617     },
49618     
49619     applyConfig : function(config){
49620         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49621         this.config = config;
49622         
49623     },
49624     
49625     /**
49626      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49627      * the width, for horizontal (north, south) the height.
49628      * @param {Number} newSize The new width or height
49629      */
49630     resizeTo : function(newSize){
49631         var el = this.el ? this.el :
49632                  (this.activePanel ? this.activePanel.getEl() : null);
49633         if(el){
49634             switch(this.position){
49635                 case "east":
49636                 case "west":
49637                     el.setWidth(newSize);
49638                     this.fireEvent("resized", this, newSize);
49639                 break;
49640                 case "north":
49641                 case "south":
49642                     el.setHeight(newSize);
49643                     this.fireEvent("resized", this, newSize);
49644                 break;                
49645             }
49646         }
49647     },
49648     
49649     getBox : function(){
49650         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49651     },
49652     
49653     getMargins : function(){
49654         return this.margins;
49655     },
49656     
49657     updateBox : function(box){
49658         this.box = box;
49659         var el = this.activePanel.getEl();
49660         el.dom.style.left = box.x + "px";
49661         el.dom.style.top = box.y + "px";
49662         this.activePanel.setSize(box.width, box.height);
49663     },
49664     
49665     /**
49666      * Returns the container element for this region.
49667      * @return {Roo.Element}
49668      */
49669     getEl : function(){
49670         return this.activePanel;
49671     },
49672     
49673     /**
49674      * Returns true if this region is currently visible.
49675      * @return {Boolean}
49676      */
49677     isVisible : function(){
49678         return this.activePanel ? true : false;
49679     },
49680     
49681     setActivePanel : function(panel){
49682         panel = this.getPanel(panel);
49683         if(this.activePanel && this.activePanel != panel){
49684             this.activePanel.setActiveState(false);
49685             this.activePanel.getEl().setLeftTop(-10000,-10000);
49686         }
49687         this.activePanel = panel;
49688         panel.setActiveState(true);
49689         if(this.box){
49690             panel.setSize(this.box.width, this.box.height);
49691         }
49692         this.fireEvent("panelactivated", this, panel);
49693         this.fireEvent("invalidated");
49694     },
49695     
49696     /**
49697      * Show the specified panel.
49698      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49699      * @return {Roo.ContentPanel} The shown panel or null
49700      */
49701     showPanel : function(panel){
49702         if(panel = this.getPanel(panel)){
49703             this.setActivePanel(panel);
49704         }
49705         return panel;
49706     },
49707     
49708     /**
49709      * Get the active panel for this region.
49710      * @return {Roo.ContentPanel} The active panel or null
49711      */
49712     getActivePanel : function(){
49713         return this.activePanel;
49714     },
49715     
49716     /**
49717      * Add the passed ContentPanel(s)
49718      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49719      * @return {Roo.ContentPanel} The panel added (if only one was added)
49720      */
49721     add : function(panel){
49722         if(arguments.length > 1){
49723             for(var i = 0, len = arguments.length; i < len; i++) {
49724                 this.add(arguments[i]);
49725             }
49726             return null;
49727         }
49728         if(this.hasPanel(panel)){
49729             this.showPanel(panel);
49730             return panel;
49731         }
49732         var el = panel.getEl();
49733         if(el.dom.parentNode != this.mgr.el.dom){
49734             this.mgr.el.dom.appendChild(el.dom);
49735         }
49736         if(panel.setRegion){
49737             panel.setRegion(this);
49738         }
49739         this.panels.add(panel);
49740         el.setStyle("position", "absolute");
49741         if(!panel.background){
49742             this.setActivePanel(panel);
49743             if(this.config.initialSize && this.panels.getCount()==1){
49744                 this.resizeTo(this.config.initialSize);
49745             }
49746         }
49747         this.fireEvent("paneladded", this, panel);
49748         return panel;
49749     },
49750     
49751     /**
49752      * Returns true if the panel is in this region.
49753      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49754      * @return {Boolean}
49755      */
49756     hasPanel : function(panel){
49757         if(typeof panel == "object"){ // must be panel obj
49758             panel = panel.getId();
49759         }
49760         return this.getPanel(panel) ? true : false;
49761     },
49762     
49763     /**
49764      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49765      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49766      * @param {Boolean} preservePanel Overrides the config preservePanel option
49767      * @return {Roo.ContentPanel} The panel that was removed
49768      */
49769     remove : function(panel, preservePanel){
49770         panel = this.getPanel(panel);
49771         if(!panel){
49772             return null;
49773         }
49774         var e = {};
49775         this.fireEvent("beforeremove", this, panel, e);
49776         if(e.cancel === true){
49777             return null;
49778         }
49779         var panelId = panel.getId();
49780         this.panels.removeKey(panelId);
49781         return panel;
49782     },
49783     
49784     /**
49785      * Returns the panel specified or null if it's not in this region.
49786      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49787      * @return {Roo.ContentPanel}
49788      */
49789     getPanel : function(id){
49790         if(typeof id == "object"){ // must be panel obj
49791             return id;
49792         }
49793         return this.panels.get(id);
49794     },
49795     
49796     /**
49797      * Returns this regions position (north/south/east/west/center).
49798      * @return {String} 
49799      */
49800     getPosition: function(){
49801         return this.position;    
49802     }
49803 });/*
49804  * Based on:
49805  * Ext JS Library 1.1.1
49806  * Copyright(c) 2006-2007, Ext JS, LLC.
49807  *
49808  * Originally Released Under LGPL - original licence link has changed is not relivant.
49809  *
49810  * Fork - LGPL
49811  * <script type="text/javascript">
49812  */
49813  
49814 /**
49815  * @class Roo.LayoutRegion
49816  * @extends Roo.BasicLayoutRegion
49817  * This class represents a region in a layout manager.
49818  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49819  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49820  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49821  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49822  * @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})
49823  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49824  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49825  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49826  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49827  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49828  * @cfg {String}    title           The title for the region (overrides panel titles)
49829  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49830  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49831  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49832  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49833  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49834  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49835  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49836  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49837  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49838  * @cfg {Boolean}   showPin         True to show a pin button
49839  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49840  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49841  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49842  * @cfg {Number}    width           For East/West panels
49843  * @cfg {Number}    height          For North/South panels
49844  * @cfg {Boolean}   split           To show the splitter
49845  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49846  */
49847 Roo.LayoutRegion = function(mgr, config, pos){
49848     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49849     var dh = Roo.DomHelper;
49850     /** This region's container element 
49851     * @type Roo.Element */
49852     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49853     /** This region's title element 
49854     * @type Roo.Element */
49855
49856     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49857         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49858         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49859     ]}, true);
49860     this.titleEl.enableDisplayMode();
49861     /** This region's title text element 
49862     * @type HTMLElement */
49863     this.titleTextEl = this.titleEl.dom.firstChild;
49864     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49865     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49866     this.closeBtn.enableDisplayMode();
49867     this.closeBtn.on("click", this.closeClicked, this);
49868     this.closeBtn.hide();
49869
49870     this.createBody(config);
49871     this.visible = true;
49872     this.collapsed = false;
49873
49874     if(config.hideWhenEmpty){
49875         this.hide();
49876         this.on("paneladded", this.validateVisibility, this);
49877         this.on("panelremoved", this.validateVisibility, this);
49878     }
49879     this.applyConfig(config);
49880 };
49881
49882 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49883
49884     createBody : function(){
49885         /** This region's body element 
49886         * @type Roo.Element */
49887         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49888     },
49889
49890     applyConfig : function(c){
49891         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49892             var dh = Roo.DomHelper;
49893             if(c.titlebar !== false){
49894                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49895                 this.collapseBtn.on("click", this.collapse, this);
49896                 this.collapseBtn.enableDisplayMode();
49897
49898                 if(c.showPin === true || this.showPin){
49899                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49900                     this.stickBtn.enableDisplayMode();
49901                     this.stickBtn.on("click", this.expand, this);
49902                     this.stickBtn.hide();
49903                 }
49904             }
49905             /** This region's collapsed element
49906             * @type Roo.Element */
49907             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49908                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49909             ]}, true);
49910             if(c.floatable !== false){
49911                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49912                this.collapsedEl.on("click", this.collapseClick, this);
49913             }
49914
49915             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49916                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49917                    id: "message", unselectable: "on", style:{"float":"left"}});
49918                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49919              }
49920             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49921             this.expandBtn.on("click", this.expand, this);
49922         }
49923         if(this.collapseBtn){
49924             this.collapseBtn.setVisible(c.collapsible == true);
49925         }
49926         this.cmargins = c.cmargins || this.cmargins ||
49927                          (this.position == "west" || this.position == "east" ?
49928                              {top: 0, left: 2, right:2, bottom: 0} :
49929                              {top: 2, left: 0, right:0, bottom: 2});
49930         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49931         this.bottomTabs = c.tabPosition != "top";
49932         this.autoScroll = c.autoScroll || false;
49933         if(this.autoScroll){
49934             this.bodyEl.setStyle("overflow", "auto");
49935         }else{
49936             this.bodyEl.setStyle("overflow", "hidden");
49937         }
49938         //if(c.titlebar !== false){
49939             if((!c.titlebar && !c.title) || c.titlebar === false){
49940                 this.titleEl.hide();
49941             }else{
49942                 this.titleEl.show();
49943                 if(c.title){
49944                     this.titleTextEl.innerHTML = c.title;
49945                 }
49946             }
49947         //}
49948         this.duration = c.duration || .30;
49949         this.slideDuration = c.slideDuration || .45;
49950         this.config = c;
49951         if(c.collapsed){
49952             this.collapse(true);
49953         }
49954         if(c.hidden){
49955             this.hide();
49956         }
49957     },
49958     /**
49959      * Returns true if this region is currently visible.
49960      * @return {Boolean}
49961      */
49962     isVisible : function(){
49963         return this.visible;
49964     },
49965
49966     /**
49967      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49968      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49969      */
49970     setCollapsedTitle : function(title){
49971         title = title || "&#160;";
49972         if(this.collapsedTitleTextEl){
49973             this.collapsedTitleTextEl.innerHTML = title;
49974         }
49975     },
49976
49977     getBox : function(){
49978         var b;
49979         if(!this.collapsed){
49980             b = this.el.getBox(false, true);
49981         }else{
49982             b = this.collapsedEl.getBox(false, true);
49983         }
49984         return b;
49985     },
49986
49987     getMargins : function(){
49988         return this.collapsed ? this.cmargins : this.margins;
49989     },
49990
49991     highlight : function(){
49992         this.el.addClass("x-layout-panel-dragover");
49993     },
49994
49995     unhighlight : function(){
49996         this.el.removeClass("x-layout-panel-dragover");
49997     },
49998
49999     updateBox : function(box){
50000         this.box = box;
50001         if(!this.collapsed){
50002             this.el.dom.style.left = box.x + "px";
50003             this.el.dom.style.top = box.y + "px";
50004             this.updateBody(box.width, box.height);
50005         }else{
50006             this.collapsedEl.dom.style.left = box.x + "px";
50007             this.collapsedEl.dom.style.top = box.y + "px";
50008             this.collapsedEl.setSize(box.width, box.height);
50009         }
50010         if(this.tabs){
50011             this.tabs.autoSizeTabs();
50012         }
50013     },
50014
50015     updateBody : function(w, h){
50016         if(w !== null){
50017             this.el.setWidth(w);
50018             w -= this.el.getBorderWidth("rl");
50019             if(this.config.adjustments){
50020                 w += this.config.adjustments[0];
50021             }
50022         }
50023         if(h !== null){
50024             this.el.setHeight(h);
50025             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50026             h -= this.el.getBorderWidth("tb");
50027             if(this.config.adjustments){
50028                 h += this.config.adjustments[1];
50029             }
50030             this.bodyEl.setHeight(h);
50031             if(this.tabs){
50032                 h = this.tabs.syncHeight(h);
50033             }
50034         }
50035         if(this.panelSize){
50036             w = w !== null ? w : this.panelSize.width;
50037             h = h !== null ? h : this.panelSize.height;
50038         }
50039         if(this.activePanel){
50040             var el = this.activePanel.getEl();
50041             w = w !== null ? w : el.getWidth();
50042             h = h !== null ? h : el.getHeight();
50043             this.panelSize = {width: w, height: h};
50044             this.activePanel.setSize(w, h);
50045         }
50046         if(Roo.isIE && this.tabs){
50047             this.tabs.el.repaint();
50048         }
50049     },
50050
50051     /**
50052      * Returns the container element for this region.
50053      * @return {Roo.Element}
50054      */
50055     getEl : function(){
50056         return this.el;
50057     },
50058
50059     /**
50060      * Hides this region.
50061      */
50062     hide : function(){
50063         if(!this.collapsed){
50064             this.el.dom.style.left = "-2000px";
50065             this.el.hide();
50066         }else{
50067             this.collapsedEl.dom.style.left = "-2000px";
50068             this.collapsedEl.hide();
50069         }
50070         this.visible = false;
50071         this.fireEvent("visibilitychange", this, false);
50072     },
50073
50074     /**
50075      * Shows this region if it was previously hidden.
50076      */
50077     show : function(){
50078         if(!this.collapsed){
50079             this.el.show();
50080         }else{
50081             this.collapsedEl.show();
50082         }
50083         this.visible = true;
50084         this.fireEvent("visibilitychange", this, true);
50085     },
50086
50087     closeClicked : function(){
50088         if(this.activePanel){
50089             this.remove(this.activePanel);
50090         }
50091     },
50092
50093     collapseClick : function(e){
50094         if(this.isSlid){
50095            e.stopPropagation();
50096            this.slideIn();
50097         }else{
50098            e.stopPropagation();
50099            this.slideOut();
50100         }
50101     },
50102
50103     /**
50104      * Collapses this region.
50105      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50106      */
50107     collapse : function(skipAnim){
50108         if(this.collapsed) return;
50109         this.collapsed = true;
50110         if(this.split){
50111             this.split.el.hide();
50112         }
50113         if(this.config.animate && skipAnim !== true){
50114             this.fireEvent("invalidated", this);
50115             this.animateCollapse();
50116         }else{
50117             this.el.setLocation(-20000,-20000);
50118             this.el.hide();
50119             this.collapsedEl.show();
50120             this.fireEvent("collapsed", this);
50121             this.fireEvent("invalidated", this);
50122         }
50123     },
50124
50125     animateCollapse : function(){
50126         // overridden
50127     },
50128
50129     /**
50130      * Expands this region if it was previously collapsed.
50131      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50132      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50133      */
50134     expand : function(e, skipAnim){
50135         if(e) e.stopPropagation();
50136         if(!this.collapsed || this.el.hasActiveFx()) return;
50137         if(this.isSlid){
50138             this.afterSlideIn();
50139             skipAnim = true;
50140         }
50141         this.collapsed = false;
50142         if(this.config.animate && skipAnim !== true){
50143             this.animateExpand();
50144         }else{
50145             this.el.show();
50146             if(this.split){
50147                 this.split.el.show();
50148             }
50149             this.collapsedEl.setLocation(-2000,-2000);
50150             this.collapsedEl.hide();
50151             this.fireEvent("invalidated", this);
50152             this.fireEvent("expanded", this);
50153         }
50154     },
50155
50156     animateExpand : function(){
50157         // overridden
50158     },
50159
50160     initTabs : function()
50161     {
50162         this.bodyEl.setStyle("overflow", "hidden");
50163         var ts = new Roo.TabPanel(
50164                 this.bodyEl.dom,
50165                 {
50166                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50167                     disableTooltips: this.config.disableTabTips,
50168                     toolbar : this.config.toolbar
50169                 }
50170         );
50171         if(this.config.hideTabs){
50172             ts.stripWrap.setDisplayed(false);
50173         }
50174         this.tabs = ts;
50175         ts.resizeTabs = this.config.resizeTabs === true;
50176         ts.minTabWidth = this.config.minTabWidth || 40;
50177         ts.maxTabWidth = this.config.maxTabWidth || 250;
50178         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50179         ts.monitorResize = false;
50180         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50181         ts.bodyEl.addClass('x-layout-tabs-body');
50182         this.panels.each(this.initPanelAsTab, this);
50183     },
50184
50185     initPanelAsTab : function(panel){
50186         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50187                     this.config.closeOnTab && panel.isClosable());
50188         if(panel.tabTip !== undefined){
50189             ti.setTooltip(panel.tabTip);
50190         }
50191         ti.on("activate", function(){
50192               this.setActivePanel(panel);
50193         }, this);
50194         if(this.config.closeOnTab){
50195             ti.on("beforeclose", function(t, e){
50196                 e.cancel = true;
50197                 this.remove(panel);
50198             }, this);
50199         }
50200         return ti;
50201     },
50202
50203     updatePanelTitle : function(panel, title){
50204         if(this.activePanel == panel){
50205             this.updateTitle(title);
50206         }
50207         if(this.tabs){
50208             var ti = this.tabs.getTab(panel.getEl().id);
50209             ti.setText(title);
50210             if(panel.tabTip !== undefined){
50211                 ti.setTooltip(panel.tabTip);
50212             }
50213         }
50214     },
50215
50216     updateTitle : function(title){
50217         if(this.titleTextEl && !this.config.title){
50218             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50219         }
50220     },
50221
50222     setActivePanel : function(panel){
50223         panel = this.getPanel(panel);
50224         if(this.activePanel && this.activePanel != panel){
50225             this.activePanel.setActiveState(false);
50226         }
50227         this.activePanel = panel;
50228         panel.setActiveState(true);
50229         if(this.panelSize){
50230             panel.setSize(this.panelSize.width, this.panelSize.height);
50231         }
50232         if(this.closeBtn){
50233             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50234         }
50235         this.updateTitle(panel.getTitle());
50236         if(this.tabs){
50237             this.fireEvent("invalidated", this);
50238         }
50239         this.fireEvent("panelactivated", this, panel);
50240     },
50241
50242     /**
50243      * Shows the specified panel.
50244      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50245      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50246      */
50247     showPanel : function(panel){
50248         if(panel = this.getPanel(panel)){
50249             if(this.tabs){
50250                 var tab = this.tabs.getTab(panel.getEl().id);
50251                 if(tab.isHidden()){
50252                     this.tabs.unhideTab(tab.id);
50253                 }
50254                 tab.activate();
50255             }else{
50256                 this.setActivePanel(panel);
50257             }
50258         }
50259         return panel;
50260     },
50261
50262     /**
50263      * Get the active panel for this region.
50264      * @return {Roo.ContentPanel} The active panel or null
50265      */
50266     getActivePanel : function(){
50267         return this.activePanel;
50268     },
50269
50270     validateVisibility : function(){
50271         if(this.panels.getCount() < 1){
50272             this.updateTitle("&#160;");
50273             this.closeBtn.hide();
50274             this.hide();
50275         }else{
50276             if(!this.isVisible()){
50277                 this.show();
50278             }
50279         }
50280     },
50281
50282     /**
50283      * Adds the passed ContentPanel(s) to this region.
50284      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50285      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50286      */
50287     add : function(panel){
50288         if(arguments.length > 1){
50289             for(var i = 0, len = arguments.length; i < len; i++) {
50290                 this.add(arguments[i]);
50291             }
50292             return null;
50293         }
50294         if(this.hasPanel(panel)){
50295             this.showPanel(panel);
50296             return panel;
50297         }
50298         panel.setRegion(this);
50299         this.panels.add(panel);
50300         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50301             this.bodyEl.dom.appendChild(panel.getEl().dom);
50302             if(panel.background !== true){
50303                 this.setActivePanel(panel);
50304             }
50305             this.fireEvent("paneladded", this, panel);
50306             return panel;
50307         }
50308         if(!this.tabs){
50309             this.initTabs();
50310         }else{
50311             this.initPanelAsTab(panel);
50312         }
50313         if(panel.background !== true){
50314             this.tabs.activate(panel.getEl().id);
50315         }
50316         this.fireEvent("paneladded", this, panel);
50317         return panel;
50318     },
50319
50320     /**
50321      * Hides the tab for the specified panel.
50322      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50323      */
50324     hidePanel : function(panel){
50325         if(this.tabs && (panel = this.getPanel(panel))){
50326             this.tabs.hideTab(panel.getEl().id);
50327         }
50328     },
50329
50330     /**
50331      * Unhides the tab for a previously hidden panel.
50332      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50333      */
50334     unhidePanel : function(panel){
50335         if(this.tabs && (panel = this.getPanel(panel))){
50336             this.tabs.unhideTab(panel.getEl().id);
50337         }
50338     },
50339
50340     clearPanels : function(){
50341         while(this.panels.getCount() > 0){
50342              this.remove(this.panels.first());
50343         }
50344     },
50345
50346     /**
50347      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50348      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50349      * @param {Boolean} preservePanel Overrides the config preservePanel option
50350      * @return {Roo.ContentPanel} The panel that was removed
50351      */
50352     remove : function(panel, preservePanel){
50353         panel = this.getPanel(panel);
50354         if(!panel){
50355             return null;
50356         }
50357         var e = {};
50358         this.fireEvent("beforeremove", this, panel, e);
50359         if(e.cancel === true){
50360             return null;
50361         }
50362         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50363         var panelId = panel.getId();
50364         this.panels.removeKey(panelId);
50365         if(preservePanel){
50366             document.body.appendChild(panel.getEl().dom);
50367         }
50368         if(this.tabs){
50369             this.tabs.removeTab(panel.getEl().id);
50370         }else if (!preservePanel){
50371             this.bodyEl.dom.removeChild(panel.getEl().dom);
50372         }
50373         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50374             var p = this.panels.first();
50375             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50376             tempEl.appendChild(p.getEl().dom);
50377             this.bodyEl.update("");
50378             this.bodyEl.dom.appendChild(p.getEl().dom);
50379             tempEl = null;
50380             this.updateTitle(p.getTitle());
50381             this.tabs = null;
50382             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50383             this.setActivePanel(p);
50384         }
50385         panel.setRegion(null);
50386         if(this.activePanel == panel){
50387             this.activePanel = null;
50388         }
50389         if(this.config.autoDestroy !== false && preservePanel !== true){
50390             try{panel.destroy();}catch(e){}
50391         }
50392         this.fireEvent("panelremoved", this, panel);
50393         return panel;
50394     },
50395
50396     /**
50397      * Returns the TabPanel component used by this region
50398      * @return {Roo.TabPanel}
50399      */
50400     getTabs : function(){
50401         return this.tabs;
50402     },
50403
50404     createTool : function(parentEl, className){
50405         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50406             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50407         btn.addClassOnOver("x-layout-tools-button-over");
50408         return btn;
50409     }
50410 });/*
50411  * Based on:
50412  * Ext JS Library 1.1.1
50413  * Copyright(c) 2006-2007, Ext JS, LLC.
50414  *
50415  * Originally Released Under LGPL - original licence link has changed is not relivant.
50416  *
50417  * Fork - LGPL
50418  * <script type="text/javascript">
50419  */
50420  
50421
50422
50423 /**
50424  * @class Roo.SplitLayoutRegion
50425  * @extends Roo.LayoutRegion
50426  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50427  */
50428 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50429     this.cursor = cursor;
50430     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50431 };
50432
50433 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50434     splitTip : "Drag to resize.",
50435     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50436     useSplitTips : false,
50437
50438     applyConfig : function(config){
50439         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50440         if(config.split){
50441             if(!this.split){
50442                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50443                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50444                 /** The SplitBar for this region 
50445                 * @type Roo.SplitBar */
50446                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50447                 this.split.on("moved", this.onSplitMove, this);
50448                 this.split.useShim = config.useShim === true;
50449                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50450                 if(this.useSplitTips){
50451                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50452                 }
50453                 if(config.collapsible){
50454                     this.split.el.on("dblclick", this.collapse,  this);
50455                 }
50456             }
50457             if(typeof config.minSize != "undefined"){
50458                 this.split.minSize = config.minSize;
50459             }
50460             if(typeof config.maxSize != "undefined"){
50461                 this.split.maxSize = config.maxSize;
50462             }
50463             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50464                 this.hideSplitter();
50465             }
50466         }
50467     },
50468
50469     getHMaxSize : function(){
50470          var cmax = this.config.maxSize || 10000;
50471          var center = this.mgr.getRegion("center");
50472          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50473     },
50474
50475     getVMaxSize : function(){
50476          var cmax = this.config.maxSize || 10000;
50477          var center = this.mgr.getRegion("center");
50478          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50479     },
50480
50481     onSplitMove : function(split, newSize){
50482         this.fireEvent("resized", this, newSize);
50483     },
50484     
50485     /** 
50486      * Returns the {@link Roo.SplitBar} for this region.
50487      * @return {Roo.SplitBar}
50488      */
50489     getSplitBar : function(){
50490         return this.split;
50491     },
50492     
50493     hide : function(){
50494         this.hideSplitter();
50495         Roo.SplitLayoutRegion.superclass.hide.call(this);
50496     },
50497
50498     hideSplitter : function(){
50499         if(this.split){
50500             this.split.el.setLocation(-2000,-2000);
50501             this.split.el.hide();
50502         }
50503     },
50504
50505     show : function(){
50506         if(this.split){
50507             this.split.el.show();
50508         }
50509         Roo.SplitLayoutRegion.superclass.show.call(this);
50510     },
50511     
50512     beforeSlide: function(){
50513         if(Roo.isGecko){// firefox overflow auto bug workaround
50514             this.bodyEl.clip();
50515             if(this.tabs) this.tabs.bodyEl.clip();
50516             if(this.activePanel){
50517                 this.activePanel.getEl().clip();
50518                 
50519                 if(this.activePanel.beforeSlide){
50520                     this.activePanel.beforeSlide();
50521                 }
50522             }
50523         }
50524     },
50525     
50526     afterSlide : function(){
50527         if(Roo.isGecko){// firefox overflow auto bug workaround
50528             this.bodyEl.unclip();
50529             if(this.tabs) this.tabs.bodyEl.unclip();
50530             if(this.activePanel){
50531                 this.activePanel.getEl().unclip();
50532                 if(this.activePanel.afterSlide){
50533                     this.activePanel.afterSlide();
50534                 }
50535             }
50536         }
50537     },
50538
50539     initAutoHide : function(){
50540         if(this.autoHide !== false){
50541             if(!this.autoHideHd){
50542                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50543                 this.autoHideHd = {
50544                     "mouseout": function(e){
50545                         if(!e.within(this.el, true)){
50546                             st.delay(500);
50547                         }
50548                     },
50549                     "mouseover" : function(e){
50550                         st.cancel();
50551                     },
50552                     scope : this
50553                 };
50554             }
50555             this.el.on(this.autoHideHd);
50556         }
50557     },
50558
50559     clearAutoHide : function(){
50560         if(this.autoHide !== false){
50561             this.el.un("mouseout", this.autoHideHd.mouseout);
50562             this.el.un("mouseover", this.autoHideHd.mouseover);
50563         }
50564     },
50565
50566     clearMonitor : function(){
50567         Roo.get(document).un("click", this.slideInIf, this);
50568     },
50569
50570     // these names are backwards but not changed for compat
50571     slideOut : function(){
50572         if(this.isSlid || this.el.hasActiveFx()){
50573             return;
50574         }
50575         this.isSlid = true;
50576         if(this.collapseBtn){
50577             this.collapseBtn.hide();
50578         }
50579         this.closeBtnState = this.closeBtn.getStyle('display');
50580         this.closeBtn.hide();
50581         if(this.stickBtn){
50582             this.stickBtn.show();
50583         }
50584         this.el.show();
50585         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50586         this.beforeSlide();
50587         this.el.setStyle("z-index", 10001);
50588         this.el.slideIn(this.getSlideAnchor(), {
50589             callback: function(){
50590                 this.afterSlide();
50591                 this.initAutoHide();
50592                 Roo.get(document).on("click", this.slideInIf, this);
50593                 this.fireEvent("slideshow", this);
50594             },
50595             scope: this,
50596             block: true
50597         });
50598     },
50599
50600     afterSlideIn : function(){
50601         this.clearAutoHide();
50602         this.isSlid = false;
50603         this.clearMonitor();
50604         this.el.setStyle("z-index", "");
50605         if(this.collapseBtn){
50606             this.collapseBtn.show();
50607         }
50608         this.closeBtn.setStyle('display', this.closeBtnState);
50609         if(this.stickBtn){
50610             this.stickBtn.hide();
50611         }
50612         this.fireEvent("slidehide", this);
50613     },
50614
50615     slideIn : function(cb){
50616         if(!this.isSlid || this.el.hasActiveFx()){
50617             Roo.callback(cb);
50618             return;
50619         }
50620         this.isSlid = false;
50621         this.beforeSlide();
50622         this.el.slideOut(this.getSlideAnchor(), {
50623             callback: function(){
50624                 this.el.setLeftTop(-10000, -10000);
50625                 this.afterSlide();
50626                 this.afterSlideIn();
50627                 Roo.callback(cb);
50628             },
50629             scope: this,
50630             block: true
50631         });
50632     },
50633     
50634     slideInIf : function(e){
50635         if(!e.within(this.el)){
50636             this.slideIn();
50637         }
50638     },
50639
50640     animateCollapse : function(){
50641         this.beforeSlide();
50642         this.el.setStyle("z-index", 20000);
50643         var anchor = this.getSlideAnchor();
50644         this.el.slideOut(anchor, {
50645             callback : function(){
50646                 this.el.setStyle("z-index", "");
50647                 this.collapsedEl.slideIn(anchor, {duration:.3});
50648                 this.afterSlide();
50649                 this.el.setLocation(-10000,-10000);
50650                 this.el.hide();
50651                 this.fireEvent("collapsed", this);
50652             },
50653             scope: this,
50654             block: true
50655         });
50656     },
50657
50658     animateExpand : function(){
50659         this.beforeSlide();
50660         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50661         this.el.setStyle("z-index", 20000);
50662         this.collapsedEl.hide({
50663             duration:.1
50664         });
50665         this.el.slideIn(this.getSlideAnchor(), {
50666             callback : function(){
50667                 this.el.setStyle("z-index", "");
50668                 this.afterSlide();
50669                 if(this.split){
50670                     this.split.el.show();
50671                 }
50672                 this.fireEvent("invalidated", this);
50673                 this.fireEvent("expanded", this);
50674             },
50675             scope: this,
50676             block: true
50677         });
50678     },
50679
50680     anchors : {
50681         "west" : "left",
50682         "east" : "right",
50683         "north" : "top",
50684         "south" : "bottom"
50685     },
50686
50687     sanchors : {
50688         "west" : "l",
50689         "east" : "r",
50690         "north" : "t",
50691         "south" : "b"
50692     },
50693
50694     canchors : {
50695         "west" : "tl-tr",
50696         "east" : "tr-tl",
50697         "north" : "tl-bl",
50698         "south" : "bl-tl"
50699     },
50700
50701     getAnchor : function(){
50702         return this.anchors[this.position];
50703     },
50704
50705     getCollapseAnchor : function(){
50706         return this.canchors[this.position];
50707     },
50708
50709     getSlideAnchor : function(){
50710         return this.sanchors[this.position];
50711     },
50712
50713     getAlignAdj : function(){
50714         var cm = this.cmargins;
50715         switch(this.position){
50716             case "west":
50717                 return [0, 0];
50718             break;
50719             case "east":
50720                 return [0, 0];
50721             break;
50722             case "north":
50723                 return [0, 0];
50724             break;
50725             case "south":
50726                 return [0, 0];
50727             break;
50728         }
50729     },
50730
50731     getExpandAdj : function(){
50732         var c = this.collapsedEl, cm = this.cmargins;
50733         switch(this.position){
50734             case "west":
50735                 return [-(cm.right+c.getWidth()+cm.left), 0];
50736             break;
50737             case "east":
50738                 return [cm.right+c.getWidth()+cm.left, 0];
50739             break;
50740             case "north":
50741                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50742             break;
50743             case "south":
50744                 return [0, cm.top+cm.bottom+c.getHeight()];
50745             break;
50746         }
50747     }
50748 });/*
50749  * Based on:
50750  * Ext JS Library 1.1.1
50751  * Copyright(c) 2006-2007, Ext JS, LLC.
50752  *
50753  * Originally Released Under LGPL - original licence link has changed is not relivant.
50754  *
50755  * Fork - LGPL
50756  * <script type="text/javascript">
50757  */
50758 /*
50759  * These classes are private internal classes
50760  */
50761 Roo.CenterLayoutRegion = function(mgr, config){
50762     Roo.LayoutRegion.call(this, mgr, config, "center");
50763     this.visible = true;
50764     this.minWidth = config.minWidth || 20;
50765     this.minHeight = config.minHeight || 20;
50766 };
50767
50768 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50769     hide : function(){
50770         // center panel can't be hidden
50771     },
50772     
50773     show : function(){
50774         // center panel can't be hidden
50775     },
50776     
50777     getMinWidth: function(){
50778         return this.minWidth;
50779     },
50780     
50781     getMinHeight: function(){
50782         return this.minHeight;
50783     }
50784 });
50785
50786
50787 Roo.NorthLayoutRegion = function(mgr, config){
50788     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50789     if(this.split){
50790         this.split.placement = Roo.SplitBar.TOP;
50791         this.split.orientation = Roo.SplitBar.VERTICAL;
50792         this.split.el.addClass("x-layout-split-v");
50793     }
50794     var size = config.initialSize || config.height;
50795     if(typeof size != "undefined"){
50796         this.el.setHeight(size);
50797     }
50798 };
50799 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50800     orientation: Roo.SplitBar.VERTICAL,
50801     getBox : function(){
50802         if(this.collapsed){
50803             return this.collapsedEl.getBox();
50804         }
50805         var box = this.el.getBox();
50806         if(this.split){
50807             box.height += this.split.el.getHeight();
50808         }
50809         return box;
50810     },
50811     
50812     updateBox : function(box){
50813         if(this.split && !this.collapsed){
50814             box.height -= this.split.el.getHeight();
50815             this.split.el.setLeft(box.x);
50816             this.split.el.setTop(box.y+box.height);
50817             this.split.el.setWidth(box.width);
50818         }
50819         if(this.collapsed){
50820             this.updateBody(box.width, null);
50821         }
50822         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50823     }
50824 });
50825
50826 Roo.SouthLayoutRegion = function(mgr, config){
50827     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50828     if(this.split){
50829         this.split.placement = Roo.SplitBar.BOTTOM;
50830         this.split.orientation = Roo.SplitBar.VERTICAL;
50831         this.split.el.addClass("x-layout-split-v");
50832     }
50833     var size = config.initialSize || config.height;
50834     if(typeof size != "undefined"){
50835         this.el.setHeight(size);
50836     }
50837 };
50838 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50839     orientation: Roo.SplitBar.VERTICAL,
50840     getBox : function(){
50841         if(this.collapsed){
50842             return this.collapsedEl.getBox();
50843         }
50844         var box = this.el.getBox();
50845         if(this.split){
50846             var sh = this.split.el.getHeight();
50847             box.height += sh;
50848             box.y -= sh;
50849         }
50850         return box;
50851     },
50852     
50853     updateBox : function(box){
50854         if(this.split && !this.collapsed){
50855             var sh = this.split.el.getHeight();
50856             box.height -= sh;
50857             box.y += sh;
50858             this.split.el.setLeft(box.x);
50859             this.split.el.setTop(box.y-sh);
50860             this.split.el.setWidth(box.width);
50861         }
50862         if(this.collapsed){
50863             this.updateBody(box.width, null);
50864         }
50865         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50866     }
50867 });
50868
50869 Roo.EastLayoutRegion = function(mgr, config){
50870     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50871     if(this.split){
50872         this.split.placement = Roo.SplitBar.RIGHT;
50873         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50874         this.split.el.addClass("x-layout-split-h");
50875     }
50876     var size = config.initialSize || config.width;
50877     if(typeof size != "undefined"){
50878         this.el.setWidth(size);
50879     }
50880 };
50881 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50882     orientation: Roo.SplitBar.HORIZONTAL,
50883     getBox : function(){
50884         if(this.collapsed){
50885             return this.collapsedEl.getBox();
50886         }
50887         var box = this.el.getBox();
50888         if(this.split){
50889             var sw = this.split.el.getWidth();
50890             box.width += sw;
50891             box.x -= sw;
50892         }
50893         return box;
50894     },
50895
50896     updateBox : function(box){
50897         if(this.split && !this.collapsed){
50898             var sw = this.split.el.getWidth();
50899             box.width -= sw;
50900             this.split.el.setLeft(box.x);
50901             this.split.el.setTop(box.y);
50902             this.split.el.setHeight(box.height);
50903             box.x += sw;
50904         }
50905         if(this.collapsed){
50906             this.updateBody(null, box.height);
50907         }
50908         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50909     }
50910 });
50911
50912 Roo.WestLayoutRegion = function(mgr, config){
50913     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50914     if(this.split){
50915         this.split.placement = Roo.SplitBar.LEFT;
50916         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50917         this.split.el.addClass("x-layout-split-h");
50918     }
50919     var size = config.initialSize || config.width;
50920     if(typeof size != "undefined"){
50921         this.el.setWidth(size);
50922     }
50923 };
50924 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50925     orientation: Roo.SplitBar.HORIZONTAL,
50926     getBox : function(){
50927         if(this.collapsed){
50928             return this.collapsedEl.getBox();
50929         }
50930         var box = this.el.getBox();
50931         if(this.split){
50932             box.width += this.split.el.getWidth();
50933         }
50934         return box;
50935     },
50936     
50937     updateBox : function(box){
50938         if(this.split && !this.collapsed){
50939             var sw = this.split.el.getWidth();
50940             box.width -= sw;
50941             this.split.el.setLeft(box.x+box.width);
50942             this.split.el.setTop(box.y);
50943             this.split.el.setHeight(box.height);
50944         }
50945         if(this.collapsed){
50946             this.updateBody(null, box.height);
50947         }
50948         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50949     }
50950 });
50951 /*
50952  * Based on:
50953  * Ext JS Library 1.1.1
50954  * Copyright(c) 2006-2007, Ext JS, LLC.
50955  *
50956  * Originally Released Under LGPL - original licence link has changed is not relivant.
50957  *
50958  * Fork - LGPL
50959  * <script type="text/javascript">
50960  */
50961  
50962  
50963 /*
50964  * Private internal class for reading and applying state
50965  */
50966 Roo.LayoutStateManager = function(layout){
50967      // default empty state
50968      this.state = {
50969         north: {},
50970         south: {},
50971         east: {},
50972         west: {}       
50973     };
50974 };
50975
50976 Roo.LayoutStateManager.prototype = {
50977     init : function(layout, provider){
50978         this.provider = provider;
50979         var state = provider.get(layout.id+"-layout-state");
50980         if(state){
50981             var wasUpdating = layout.isUpdating();
50982             if(!wasUpdating){
50983                 layout.beginUpdate();
50984             }
50985             for(var key in state){
50986                 if(typeof state[key] != "function"){
50987                     var rstate = state[key];
50988                     var r = layout.getRegion(key);
50989                     if(r && rstate){
50990                         if(rstate.size){
50991                             r.resizeTo(rstate.size);
50992                         }
50993                         if(rstate.collapsed == true){
50994                             r.collapse(true);
50995                         }else{
50996                             r.expand(null, true);
50997                         }
50998                     }
50999                 }
51000             }
51001             if(!wasUpdating){
51002                 layout.endUpdate();
51003             }
51004             this.state = state; 
51005         }
51006         this.layout = layout;
51007         layout.on("regionresized", this.onRegionResized, this);
51008         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51009         layout.on("regionexpanded", this.onRegionExpanded, this);
51010     },
51011     
51012     storeState : function(){
51013         this.provider.set(this.layout.id+"-layout-state", this.state);
51014     },
51015     
51016     onRegionResized : function(region, newSize){
51017         this.state[region.getPosition()].size = newSize;
51018         this.storeState();
51019     },
51020     
51021     onRegionCollapsed : function(region){
51022         this.state[region.getPosition()].collapsed = true;
51023         this.storeState();
51024     },
51025     
51026     onRegionExpanded : function(region){
51027         this.state[region.getPosition()].collapsed = false;
51028         this.storeState();
51029     }
51030 };/*
51031  * Based on:
51032  * Ext JS Library 1.1.1
51033  * Copyright(c) 2006-2007, Ext JS, LLC.
51034  *
51035  * Originally Released Under LGPL - original licence link has changed is not relivant.
51036  *
51037  * Fork - LGPL
51038  * <script type="text/javascript">
51039  */
51040 /**
51041  * @class Roo.ContentPanel
51042  * @extends Roo.util.Observable
51043  * A basic ContentPanel element.
51044  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51045  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51046  * @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
51047  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51048  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51049  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51050  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51051  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51052  * @cfg {String} title          The title for this panel
51053  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51054  * @cfg {String} url            Calls {@link #setUrl} with this value
51055  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51056  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51057  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51058  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51059
51060  * @constructor
51061  * Create a new ContentPanel.
51062  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51063  * @param {String/Object} config A string to set only the title or a config object
51064  * @param {String} content (optional) Set the HTML content for this panel
51065  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51066  */
51067 Roo.ContentPanel = function(el, config, content){
51068     
51069      
51070     /*
51071     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51072         config = el;
51073         el = Roo.id();
51074     }
51075     if (config && config.parentLayout) { 
51076         el = config.parentLayout.el.createChild(); 
51077     }
51078     */
51079     if(el.autoCreate){ // xtype is available if this is called from factory
51080         config = el;
51081         el = Roo.id();
51082     }
51083     this.el = Roo.get(el);
51084     if(!this.el && config && config.autoCreate){
51085         if(typeof config.autoCreate == "object"){
51086             if(!config.autoCreate.id){
51087                 config.autoCreate.id = config.id||el;
51088             }
51089             this.el = Roo.DomHelper.append(document.body,
51090                         config.autoCreate, true);
51091         }else{
51092             this.el = Roo.DomHelper.append(document.body,
51093                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51094         }
51095     }
51096     this.closable = false;
51097     this.loaded = false;
51098     this.active = false;
51099     if(typeof config == "string"){
51100         this.title = config;
51101     }else{
51102         Roo.apply(this, config);
51103     }
51104     
51105     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51106         this.wrapEl = this.el.wrap();
51107         this.toolbar.container = this.el.insertSibling(false, 'before');
51108         this.toolbar = new Roo.Toolbar(this.toolbar);
51109     }
51110     
51111     // xtype created footer. - not sure if will work as we normally have to render first..
51112     if (this.footer && !this.footer.el && this.footer.xtype) {
51113         if (!this.wrapEl) {
51114             this.wrapEl = this.el.wrap();
51115         }
51116     
51117         this.footer.container = this.wrapEl.createChild();
51118          
51119         this.footer = Roo.factory(this.footer, Roo);
51120         
51121     }
51122     
51123     if(this.resizeEl){
51124         this.resizeEl = Roo.get(this.resizeEl, true);
51125     }else{
51126         this.resizeEl = this.el;
51127     }
51128     // handle view.xtype
51129     
51130  
51131     
51132     
51133     this.addEvents({
51134         /**
51135          * @event activate
51136          * Fires when this panel is activated. 
51137          * @param {Roo.ContentPanel} this
51138          */
51139         "activate" : true,
51140         /**
51141          * @event deactivate
51142          * Fires when this panel is activated. 
51143          * @param {Roo.ContentPanel} this
51144          */
51145         "deactivate" : true,
51146
51147         /**
51148          * @event resize
51149          * Fires when this panel is resized if fitToFrame is true.
51150          * @param {Roo.ContentPanel} this
51151          * @param {Number} width The width after any component adjustments
51152          * @param {Number} height The height after any component adjustments
51153          */
51154         "resize" : true,
51155         
51156          /**
51157          * @event render
51158          * Fires when this tab is created
51159          * @param {Roo.ContentPanel} this
51160          */
51161         "render" : true
51162         
51163         
51164         
51165     });
51166     
51167
51168     
51169     
51170     if(this.autoScroll){
51171         this.resizeEl.setStyle("overflow", "auto");
51172     } else {
51173         // fix randome scrolling
51174         this.el.on('scroll', function() {
51175             Roo.log('fix random scolling');
51176             this.scrollTo('top',0); 
51177         });
51178     }
51179     content = content || this.content;
51180     if(content){
51181         this.setContent(content);
51182     }
51183     if(config && config.url){
51184         this.setUrl(this.url, this.params, this.loadOnce);
51185     }
51186     
51187     
51188     
51189     Roo.ContentPanel.superclass.constructor.call(this);
51190     
51191     if (this.view && typeof(this.view.xtype) != 'undefined') {
51192         this.view.el = this.el.appendChild(document.createElement("div"));
51193         this.view = Roo.factory(this.view); 
51194         this.view.render  &&  this.view.render(false, '');  
51195     }
51196     
51197     
51198     this.fireEvent('render', this);
51199 };
51200
51201 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51202     tabTip:'',
51203     setRegion : function(region){
51204         this.region = region;
51205         if(region){
51206            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51207         }else{
51208            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51209         } 
51210     },
51211     
51212     /**
51213      * Returns the toolbar for this Panel if one was configured. 
51214      * @return {Roo.Toolbar} 
51215      */
51216     getToolbar : function(){
51217         return this.toolbar;
51218     },
51219     
51220     setActiveState : function(active){
51221         this.active = active;
51222         if(!active){
51223             this.fireEvent("deactivate", this);
51224         }else{
51225             this.fireEvent("activate", this);
51226         }
51227     },
51228     /**
51229      * Updates this panel's element
51230      * @param {String} content The new content
51231      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51232     */
51233     setContent : function(content, loadScripts){
51234         this.el.update(content, loadScripts);
51235     },
51236
51237     ignoreResize : function(w, h){
51238         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51239             return true;
51240         }else{
51241             this.lastSize = {width: w, height: h};
51242             return false;
51243         }
51244     },
51245     /**
51246      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51247      * @return {Roo.UpdateManager} The UpdateManager
51248      */
51249     getUpdateManager : function(){
51250         return this.el.getUpdateManager();
51251     },
51252      /**
51253      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51254      * @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:
51255 <pre><code>
51256 panel.load({
51257     url: "your-url.php",
51258     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51259     callback: yourFunction,
51260     scope: yourObject, //(optional scope)
51261     discardUrl: false,
51262     nocache: false,
51263     text: "Loading...",
51264     timeout: 30,
51265     scripts: false
51266 });
51267 </code></pre>
51268      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51269      * 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.
51270      * @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}
51271      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51272      * @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.
51273      * @return {Roo.ContentPanel} this
51274      */
51275     load : function(){
51276         var um = this.el.getUpdateManager();
51277         um.update.apply(um, arguments);
51278         return this;
51279     },
51280
51281
51282     /**
51283      * 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.
51284      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51285      * @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)
51286      * @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)
51287      * @return {Roo.UpdateManager} The UpdateManager
51288      */
51289     setUrl : function(url, params, loadOnce){
51290         if(this.refreshDelegate){
51291             this.removeListener("activate", this.refreshDelegate);
51292         }
51293         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51294         this.on("activate", this.refreshDelegate);
51295         return this.el.getUpdateManager();
51296     },
51297     
51298     _handleRefresh : function(url, params, loadOnce){
51299         if(!loadOnce || !this.loaded){
51300             var updater = this.el.getUpdateManager();
51301             updater.update(url, params, this._setLoaded.createDelegate(this));
51302         }
51303     },
51304     
51305     _setLoaded : function(){
51306         this.loaded = true;
51307     }, 
51308     
51309     /**
51310      * Returns this panel's id
51311      * @return {String} 
51312      */
51313     getId : function(){
51314         return this.el.id;
51315     },
51316     
51317     /** 
51318      * Returns this panel's element - used by regiosn to add.
51319      * @return {Roo.Element} 
51320      */
51321     getEl : function(){
51322         return this.wrapEl || this.el;
51323     },
51324     
51325     adjustForComponents : function(width, height)
51326     {
51327         //Roo.log('adjustForComponents ');
51328         if(this.resizeEl != this.el){
51329             width -= this.el.getFrameWidth('lr');
51330             height -= this.el.getFrameWidth('tb');
51331         }
51332         if(this.toolbar){
51333             var te = this.toolbar.getEl();
51334             height -= te.getHeight();
51335             te.setWidth(width);
51336         }
51337         if(this.footer){
51338             var te = this.footer.getEl();
51339             Roo.log("footer:" + te.getHeight());
51340             
51341             height -= te.getHeight();
51342             te.setWidth(width);
51343         }
51344         
51345         
51346         if(this.adjustments){
51347             width += this.adjustments[0];
51348             height += this.adjustments[1];
51349         }
51350         return {"width": width, "height": height};
51351     },
51352     
51353     setSize : function(width, height){
51354         if(this.fitToFrame && !this.ignoreResize(width, height)){
51355             if(this.fitContainer && this.resizeEl != this.el){
51356                 this.el.setSize(width, height);
51357             }
51358             var size = this.adjustForComponents(width, height);
51359             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51360             this.fireEvent('resize', this, size.width, size.height);
51361         }
51362     },
51363     
51364     /**
51365      * Returns this panel's title
51366      * @return {String} 
51367      */
51368     getTitle : function(){
51369         return this.title;
51370     },
51371     
51372     /**
51373      * Set this panel's title
51374      * @param {String} title
51375      */
51376     setTitle : function(title){
51377         this.title = title;
51378         if(this.region){
51379             this.region.updatePanelTitle(this, title);
51380         }
51381     },
51382     
51383     /**
51384      * Returns true is this panel was configured to be closable
51385      * @return {Boolean} 
51386      */
51387     isClosable : function(){
51388         return this.closable;
51389     },
51390     
51391     beforeSlide : function(){
51392         this.el.clip();
51393         this.resizeEl.clip();
51394     },
51395     
51396     afterSlide : function(){
51397         this.el.unclip();
51398         this.resizeEl.unclip();
51399     },
51400     
51401     /**
51402      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51403      *   Will fail silently if the {@link #setUrl} method has not been called.
51404      *   This does not activate the panel, just updates its content.
51405      */
51406     refresh : function(){
51407         if(this.refreshDelegate){
51408            this.loaded = false;
51409            this.refreshDelegate();
51410         }
51411     },
51412     
51413     /**
51414      * Destroys this panel
51415      */
51416     destroy : function(){
51417         this.el.removeAllListeners();
51418         var tempEl = document.createElement("span");
51419         tempEl.appendChild(this.el.dom);
51420         tempEl.innerHTML = "";
51421         this.el.remove();
51422         this.el = null;
51423     },
51424     
51425     /**
51426      * form - if the content panel contains a form - this is a reference to it.
51427      * @type {Roo.form.Form}
51428      */
51429     form : false,
51430     /**
51431      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51432      *    This contains a reference to it.
51433      * @type {Roo.View}
51434      */
51435     view : false,
51436     
51437       /**
51438      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51439      * <pre><code>
51440
51441 layout.addxtype({
51442        xtype : 'Form',
51443        items: [ .... ]
51444    }
51445 );
51446
51447 </code></pre>
51448      * @param {Object} cfg Xtype definition of item to add.
51449      */
51450     
51451     addxtype : function(cfg) {
51452         // add form..
51453         if (cfg.xtype.match(/^Form$/)) {
51454             
51455             var el;
51456             //if (this.footer) {
51457             //    el = this.footer.container.insertSibling(false, 'before');
51458             //} else {
51459                 el = this.el.createChild();
51460             //}
51461
51462             this.form = new  Roo.form.Form(cfg);
51463             
51464             
51465             if ( this.form.allItems.length) this.form.render(el.dom);
51466             return this.form;
51467         }
51468         // should only have one of theses..
51469         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51470             // views.. should not be just added - used named prop 'view''
51471             
51472             cfg.el = this.el.appendChild(document.createElement("div"));
51473             // factory?
51474             
51475             var ret = new Roo.factory(cfg);
51476              
51477              ret.render && ret.render(false, ''); // render blank..
51478             this.view = ret;
51479             return ret;
51480         }
51481         return false;
51482     }
51483 });
51484
51485 /**
51486  * @class Roo.GridPanel
51487  * @extends Roo.ContentPanel
51488  * @constructor
51489  * Create a new GridPanel.
51490  * @param {Roo.grid.Grid} grid The grid for this panel
51491  * @param {String/Object} config A string to set only the panel's title, or a config object
51492  */
51493 Roo.GridPanel = function(grid, config){
51494     
51495   
51496     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51497         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51498         
51499     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51500     
51501     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51502     
51503     if(this.toolbar){
51504         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51505     }
51506     // xtype created footer. - not sure if will work as we normally have to render first..
51507     if (this.footer && !this.footer.el && this.footer.xtype) {
51508         
51509         this.footer.container = this.grid.getView().getFooterPanel(true);
51510         this.footer.dataSource = this.grid.dataSource;
51511         this.footer = Roo.factory(this.footer, Roo);
51512         
51513     }
51514     
51515     grid.monitorWindowResize = false; // turn off autosizing
51516     grid.autoHeight = false;
51517     grid.autoWidth = false;
51518     this.grid = grid;
51519     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51520 };
51521
51522 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51523     getId : function(){
51524         return this.grid.id;
51525     },
51526     
51527     /**
51528      * Returns the grid for this panel
51529      * @return {Roo.grid.Grid} 
51530      */
51531     getGrid : function(){
51532         return this.grid;    
51533     },
51534     
51535     setSize : function(width, height){
51536         if(!this.ignoreResize(width, height)){
51537             var grid = this.grid;
51538             var size = this.adjustForComponents(width, height);
51539             grid.getGridEl().setSize(size.width, size.height);
51540             grid.autoSize();
51541         }
51542     },
51543     
51544     beforeSlide : function(){
51545         this.grid.getView().scroller.clip();
51546     },
51547     
51548     afterSlide : function(){
51549         this.grid.getView().scroller.unclip();
51550     },
51551     
51552     destroy : function(){
51553         this.grid.destroy();
51554         delete this.grid;
51555         Roo.GridPanel.superclass.destroy.call(this); 
51556     }
51557 });
51558
51559
51560 /**
51561  * @class Roo.NestedLayoutPanel
51562  * @extends Roo.ContentPanel
51563  * @constructor
51564  * Create a new NestedLayoutPanel.
51565  * 
51566  * 
51567  * @param {Roo.BorderLayout} layout The layout for this panel
51568  * @param {String/Object} config A string to set only the title or a config object
51569  */
51570 Roo.NestedLayoutPanel = function(layout, config)
51571 {
51572     // construct with only one argument..
51573     /* FIXME - implement nicer consturctors
51574     if (layout.layout) {
51575         config = layout;
51576         layout = config.layout;
51577         delete config.layout;
51578     }
51579     if (layout.xtype && !layout.getEl) {
51580         // then layout needs constructing..
51581         layout = Roo.factory(layout, Roo);
51582     }
51583     */
51584     
51585     
51586     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51587     
51588     layout.monitorWindowResize = false; // turn off autosizing
51589     this.layout = layout;
51590     this.layout.getEl().addClass("x-layout-nested-layout");
51591     
51592     
51593     
51594     
51595 };
51596
51597 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51598
51599     setSize : function(width, height){
51600         if(!this.ignoreResize(width, height)){
51601             var size = this.adjustForComponents(width, height);
51602             var el = this.layout.getEl();
51603             el.setSize(size.width, size.height);
51604             var touch = el.dom.offsetWidth;
51605             this.layout.layout();
51606             // ie requires a double layout on the first pass
51607             if(Roo.isIE && !this.initialized){
51608                 this.initialized = true;
51609                 this.layout.layout();
51610             }
51611         }
51612     },
51613     
51614     // activate all subpanels if not currently active..
51615     
51616     setActiveState : function(active){
51617         this.active = active;
51618         if(!active){
51619             this.fireEvent("deactivate", this);
51620             return;
51621         }
51622         
51623         this.fireEvent("activate", this);
51624         // not sure if this should happen before or after..
51625         if (!this.layout) {
51626             return; // should not happen..
51627         }
51628         var reg = false;
51629         for (var r in this.layout.regions) {
51630             reg = this.layout.getRegion(r);
51631             if (reg.getActivePanel()) {
51632                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51633                 reg.setActivePanel(reg.getActivePanel());
51634                 continue;
51635             }
51636             if (!reg.panels.length) {
51637                 continue;
51638             }
51639             reg.showPanel(reg.getPanel(0));
51640         }
51641         
51642         
51643         
51644         
51645     },
51646     
51647     /**
51648      * Returns the nested BorderLayout for this panel
51649      * @return {Roo.BorderLayout} 
51650      */
51651     getLayout : function(){
51652         return this.layout;
51653     },
51654     
51655      /**
51656      * Adds a xtype elements to the layout of the nested panel
51657      * <pre><code>
51658
51659 panel.addxtype({
51660        xtype : 'ContentPanel',
51661        region: 'west',
51662        items: [ .... ]
51663    }
51664 );
51665
51666 panel.addxtype({
51667         xtype : 'NestedLayoutPanel',
51668         region: 'west',
51669         layout: {
51670            center: { },
51671            west: { }   
51672         },
51673         items : [ ... list of content panels or nested layout panels.. ]
51674    }
51675 );
51676 </code></pre>
51677      * @param {Object} cfg Xtype definition of item to add.
51678      */
51679     addxtype : function(cfg) {
51680         return this.layout.addxtype(cfg);
51681     
51682     }
51683 });
51684
51685 Roo.ScrollPanel = function(el, config, content){
51686     config = config || {};
51687     config.fitToFrame = true;
51688     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51689     
51690     this.el.dom.style.overflow = "hidden";
51691     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51692     this.el.removeClass("x-layout-inactive-content");
51693     this.el.on("mousewheel", this.onWheel, this);
51694
51695     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51696     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51697     up.unselectable(); down.unselectable();
51698     up.on("click", this.scrollUp, this);
51699     down.on("click", this.scrollDown, this);
51700     up.addClassOnOver("x-scroller-btn-over");
51701     down.addClassOnOver("x-scroller-btn-over");
51702     up.addClassOnClick("x-scroller-btn-click");
51703     down.addClassOnClick("x-scroller-btn-click");
51704     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51705
51706     this.resizeEl = this.el;
51707     this.el = wrap; this.up = up; this.down = down;
51708 };
51709
51710 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51711     increment : 100,
51712     wheelIncrement : 5,
51713     scrollUp : function(){
51714         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51715     },
51716
51717     scrollDown : function(){
51718         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51719     },
51720
51721     afterScroll : function(){
51722         var el = this.resizeEl;
51723         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51724         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51725         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51726     },
51727
51728     setSize : function(){
51729         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51730         this.afterScroll();
51731     },
51732
51733     onWheel : function(e){
51734         var d = e.getWheelDelta();
51735         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51736         this.afterScroll();
51737         e.stopEvent();
51738     },
51739
51740     setContent : function(content, loadScripts){
51741         this.resizeEl.update(content, loadScripts);
51742     }
51743
51744 });
51745
51746
51747
51748
51749
51750
51751
51752
51753
51754 /**
51755  * @class Roo.TreePanel
51756  * @extends Roo.ContentPanel
51757  * @constructor
51758  * Create a new TreePanel. - defaults to fit/scoll contents.
51759  * @param {String/Object} config A string to set only the panel's title, or a config object
51760  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51761  */
51762 Roo.TreePanel = function(config){
51763     var el = config.el;
51764     var tree = config.tree;
51765     delete config.tree; 
51766     delete config.el; // hopefull!
51767     
51768     // wrapper for IE7 strict & safari scroll issue
51769     
51770     var treeEl = el.createChild();
51771     config.resizeEl = treeEl;
51772     
51773     
51774     
51775     Roo.TreePanel.superclass.constructor.call(this, el, config);
51776  
51777  
51778     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51779     //console.log(tree);
51780     this.on('activate', function()
51781     {
51782         if (this.tree.rendered) {
51783             return;
51784         }
51785         //console.log('render tree');
51786         this.tree.render();
51787     });
51788     // this should not be needed.. - it's actually the 'el' that resizes?
51789     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51790     
51791     //this.on('resize',  function (cp, w, h) {
51792     //        this.tree.innerCt.setWidth(w);
51793     //        this.tree.innerCt.setHeight(h);
51794     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51795     //});
51796
51797         
51798     
51799 };
51800
51801 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51802     fitToFrame : true,
51803     autoScroll : true
51804 });
51805
51806
51807
51808
51809
51810
51811
51812
51813
51814
51815
51816 /*
51817  * Based on:
51818  * Ext JS Library 1.1.1
51819  * Copyright(c) 2006-2007, Ext JS, LLC.
51820  *
51821  * Originally Released Under LGPL - original licence link has changed is not relivant.
51822  *
51823  * Fork - LGPL
51824  * <script type="text/javascript">
51825  */
51826  
51827
51828 /**
51829  * @class Roo.ReaderLayout
51830  * @extends Roo.BorderLayout
51831  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51832  * center region containing two nested regions (a top one for a list view and one for item preview below),
51833  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51834  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51835  * expedites the setup of the overall layout and regions for this common application style.
51836  * Example:
51837  <pre><code>
51838 var reader = new Roo.ReaderLayout();
51839 var CP = Roo.ContentPanel;  // shortcut for adding
51840
51841 reader.beginUpdate();
51842 reader.add("north", new CP("north", "North"));
51843 reader.add("west", new CP("west", {title: "West"}));
51844 reader.add("east", new CP("east", {title: "East"}));
51845
51846 reader.regions.listView.add(new CP("listView", "List"));
51847 reader.regions.preview.add(new CP("preview", "Preview"));
51848 reader.endUpdate();
51849 </code></pre>
51850 * @constructor
51851 * Create a new ReaderLayout
51852 * @param {Object} config Configuration options
51853 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51854 * document.body if omitted)
51855 */
51856 Roo.ReaderLayout = function(config, renderTo){
51857     var c = config || {size:{}};
51858     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51859         north: c.north !== false ? Roo.apply({
51860             split:false,
51861             initialSize: 32,
51862             titlebar: false
51863         }, c.north) : false,
51864         west: c.west !== false ? Roo.apply({
51865             split:true,
51866             initialSize: 200,
51867             minSize: 175,
51868             maxSize: 400,
51869             titlebar: true,
51870             collapsible: true,
51871             animate: true,
51872             margins:{left:5,right:0,bottom:5,top:5},
51873             cmargins:{left:5,right:5,bottom:5,top:5}
51874         }, c.west) : false,
51875         east: c.east !== false ? Roo.apply({
51876             split:true,
51877             initialSize: 200,
51878             minSize: 175,
51879             maxSize: 400,
51880             titlebar: true,
51881             collapsible: true,
51882             animate: true,
51883             margins:{left:0,right:5,bottom:5,top:5},
51884             cmargins:{left:5,right:5,bottom:5,top:5}
51885         }, c.east) : false,
51886         center: Roo.apply({
51887             tabPosition: 'top',
51888             autoScroll:false,
51889             closeOnTab: true,
51890             titlebar:false,
51891             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51892         }, c.center)
51893     });
51894
51895     this.el.addClass('x-reader');
51896
51897     this.beginUpdate();
51898
51899     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51900         south: c.preview !== false ? Roo.apply({
51901             split:true,
51902             initialSize: 200,
51903             minSize: 100,
51904             autoScroll:true,
51905             collapsible:true,
51906             titlebar: true,
51907             cmargins:{top:5,left:0, right:0, bottom:0}
51908         }, c.preview) : false,
51909         center: Roo.apply({
51910             autoScroll:false,
51911             titlebar:false,
51912             minHeight:200
51913         }, c.listView)
51914     });
51915     this.add('center', new Roo.NestedLayoutPanel(inner,
51916             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51917
51918     this.endUpdate();
51919
51920     this.regions.preview = inner.getRegion('south');
51921     this.regions.listView = inner.getRegion('center');
51922 };
51923
51924 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51925  * Based on:
51926  * Ext JS Library 1.1.1
51927  * Copyright(c) 2006-2007, Ext JS, LLC.
51928  *
51929  * Originally Released Under LGPL - original licence link has changed is not relivant.
51930  *
51931  * Fork - LGPL
51932  * <script type="text/javascript">
51933  */
51934  
51935 /**
51936  * @class Roo.grid.Grid
51937  * @extends Roo.util.Observable
51938  * This class represents the primary interface of a component based grid control.
51939  * <br><br>Usage:<pre><code>
51940  var grid = new Roo.grid.Grid("my-container-id", {
51941      ds: myDataStore,
51942      cm: myColModel,
51943      selModel: mySelectionModel,
51944      autoSizeColumns: true,
51945      monitorWindowResize: false,
51946      trackMouseOver: true
51947  });
51948  // set any options
51949  grid.render();
51950  * </code></pre>
51951  * <b>Common Problems:</b><br/>
51952  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51953  * element will correct this<br/>
51954  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51955  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51956  * are unpredictable.<br/>
51957  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51958  * grid to calculate dimensions/offsets.<br/>
51959   * @constructor
51960  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51961  * The container MUST have some type of size defined for the grid to fill. The container will be
51962  * automatically set to position relative if it isn't already.
51963  * @param {Object} config A config object that sets properties on this grid.
51964  */
51965 Roo.grid.Grid = function(container, config){
51966         // initialize the container
51967         this.container = Roo.get(container);
51968         this.container.update("");
51969         this.container.setStyle("overflow", "hidden");
51970     this.container.addClass('x-grid-container');
51971
51972     this.id = this.container.id;
51973
51974     Roo.apply(this, config);
51975     // check and correct shorthanded configs
51976     if(this.ds){
51977         this.dataSource = this.ds;
51978         delete this.ds;
51979     }
51980     if(this.cm){
51981         this.colModel = this.cm;
51982         delete this.cm;
51983     }
51984     if(this.sm){
51985         this.selModel = this.sm;
51986         delete this.sm;
51987     }
51988
51989     if (this.selModel) {
51990         this.selModel = Roo.factory(this.selModel, Roo.grid);
51991         this.sm = this.selModel;
51992         this.sm.xmodule = this.xmodule || false;
51993     }
51994     if (typeof(this.colModel.config) == 'undefined') {
51995         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51996         this.cm = this.colModel;
51997         this.cm.xmodule = this.xmodule || false;
51998     }
51999     if (this.dataSource) {
52000         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52001         this.ds = this.dataSource;
52002         this.ds.xmodule = this.xmodule || false;
52003          
52004     }
52005     
52006     
52007     
52008     if(this.width){
52009         this.container.setWidth(this.width);
52010     }
52011
52012     if(this.height){
52013         this.container.setHeight(this.height);
52014     }
52015     /** @private */
52016         this.addEvents({
52017         // raw events
52018         /**
52019          * @event click
52020          * The raw click event for the entire grid.
52021          * @param {Roo.EventObject} e
52022          */
52023         "click" : true,
52024         /**
52025          * @event dblclick
52026          * The raw dblclick event for the entire grid.
52027          * @param {Roo.EventObject} e
52028          */
52029         "dblclick" : true,
52030         /**
52031          * @event contextmenu
52032          * The raw contextmenu event for the entire grid.
52033          * @param {Roo.EventObject} e
52034          */
52035         "contextmenu" : true,
52036         /**
52037          * @event mousedown
52038          * The raw mousedown event for the entire grid.
52039          * @param {Roo.EventObject} e
52040          */
52041         "mousedown" : true,
52042         /**
52043          * @event mouseup
52044          * The raw mouseup event for the entire grid.
52045          * @param {Roo.EventObject} e
52046          */
52047         "mouseup" : true,
52048         /**
52049          * @event mouseover
52050          * The raw mouseover event for the entire grid.
52051          * @param {Roo.EventObject} e
52052          */
52053         "mouseover" : true,
52054         /**
52055          * @event mouseout
52056          * The raw mouseout event for the entire grid.
52057          * @param {Roo.EventObject} e
52058          */
52059         "mouseout" : true,
52060         /**
52061          * @event keypress
52062          * The raw keypress event for the entire grid.
52063          * @param {Roo.EventObject} e
52064          */
52065         "keypress" : true,
52066         /**
52067          * @event keydown
52068          * The raw keydown event for the entire grid.
52069          * @param {Roo.EventObject} e
52070          */
52071         "keydown" : true,
52072
52073         // custom events
52074
52075         /**
52076          * @event cellclick
52077          * Fires when a cell is clicked
52078          * @param {Grid} this
52079          * @param {Number} rowIndex
52080          * @param {Number} columnIndex
52081          * @param {Roo.EventObject} e
52082          */
52083         "cellclick" : true,
52084         /**
52085          * @event celldblclick
52086          * Fires when a cell is double clicked
52087          * @param {Grid} this
52088          * @param {Number} rowIndex
52089          * @param {Number} columnIndex
52090          * @param {Roo.EventObject} e
52091          */
52092         "celldblclick" : true,
52093         /**
52094          * @event rowclick
52095          * Fires when a row is clicked
52096          * @param {Grid} this
52097          * @param {Number} rowIndex
52098          * @param {Roo.EventObject} e
52099          */
52100         "rowclick" : true,
52101         /**
52102          * @event rowdblclick
52103          * Fires when a row is double clicked
52104          * @param {Grid} this
52105          * @param {Number} rowIndex
52106          * @param {Roo.EventObject} e
52107          */
52108         "rowdblclick" : true,
52109         /**
52110          * @event headerclick
52111          * Fires when a header is clicked
52112          * @param {Grid} this
52113          * @param {Number} columnIndex
52114          * @param {Roo.EventObject} e
52115          */
52116         "headerclick" : true,
52117         /**
52118          * @event headerdblclick
52119          * Fires when a header cell is double clicked
52120          * @param {Grid} this
52121          * @param {Number} columnIndex
52122          * @param {Roo.EventObject} e
52123          */
52124         "headerdblclick" : true,
52125         /**
52126          * @event rowcontextmenu
52127          * Fires when a row is right clicked
52128          * @param {Grid} this
52129          * @param {Number} rowIndex
52130          * @param {Roo.EventObject} e
52131          */
52132         "rowcontextmenu" : true,
52133         /**
52134          * @event cellcontextmenu
52135          * Fires when a cell is right clicked
52136          * @param {Grid} this
52137          * @param {Number} rowIndex
52138          * @param {Number} cellIndex
52139          * @param {Roo.EventObject} e
52140          */
52141          "cellcontextmenu" : true,
52142         /**
52143          * @event headercontextmenu
52144          * Fires when a header is right clicked
52145          * @param {Grid} this
52146          * @param {Number} columnIndex
52147          * @param {Roo.EventObject} e
52148          */
52149         "headercontextmenu" : true,
52150         /**
52151          * @event bodyscroll
52152          * Fires when the body element is scrolled
52153          * @param {Number} scrollLeft
52154          * @param {Number} scrollTop
52155          */
52156         "bodyscroll" : true,
52157         /**
52158          * @event columnresize
52159          * Fires when the user resizes a column
52160          * @param {Number} columnIndex
52161          * @param {Number} newSize
52162          */
52163         "columnresize" : true,
52164         /**
52165          * @event columnmove
52166          * Fires when the user moves a column
52167          * @param {Number} oldIndex
52168          * @param {Number} newIndex
52169          */
52170         "columnmove" : true,
52171         /**
52172          * @event startdrag
52173          * Fires when row(s) start being dragged
52174          * @param {Grid} this
52175          * @param {Roo.GridDD} dd The drag drop object
52176          * @param {event} e The raw browser event
52177          */
52178         "startdrag" : true,
52179         /**
52180          * @event enddrag
52181          * Fires when a drag operation is complete
52182          * @param {Grid} this
52183          * @param {Roo.GridDD} dd The drag drop object
52184          * @param {event} e The raw browser event
52185          */
52186         "enddrag" : true,
52187         /**
52188          * @event dragdrop
52189          * Fires when dragged row(s) are dropped on a valid DD target
52190          * @param {Grid} this
52191          * @param {Roo.GridDD} dd The drag drop object
52192          * @param {String} targetId The target drag drop object
52193          * @param {event} e The raw browser event
52194          */
52195         "dragdrop" : true,
52196         /**
52197          * @event dragover
52198          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52199          * @param {Grid} this
52200          * @param {Roo.GridDD} dd The drag drop object
52201          * @param {String} targetId The target drag drop object
52202          * @param {event} e The raw browser event
52203          */
52204         "dragover" : true,
52205         /**
52206          * @event dragenter
52207          *  Fires when the dragged row(s) first cross another DD target while being dragged
52208          * @param {Grid} this
52209          * @param {Roo.GridDD} dd The drag drop object
52210          * @param {String} targetId The target drag drop object
52211          * @param {event} e The raw browser event
52212          */
52213         "dragenter" : true,
52214         /**
52215          * @event dragout
52216          * Fires when the dragged row(s) leave another DD target while being dragged
52217          * @param {Grid} this
52218          * @param {Roo.GridDD} dd The drag drop object
52219          * @param {String} targetId The target drag drop object
52220          * @param {event} e The raw browser event
52221          */
52222         "dragout" : true,
52223         /**
52224          * @event rowclass
52225          * Fires when a row is rendered, so you can change add a style to it.
52226          * @param {GridView} gridview   The grid view
52227          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52228          */
52229         'rowclass' : true,
52230
52231         /**
52232          * @event render
52233          * Fires when the grid is rendered
52234          * @param {Grid} grid
52235          */
52236         'render' : true
52237     });
52238
52239     Roo.grid.Grid.superclass.constructor.call(this);
52240 };
52241 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52242     
52243     /**
52244      * @cfg {String} ddGroup - drag drop group.
52245      */
52246
52247     /**
52248      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52249      */
52250     minColumnWidth : 25,
52251
52252     /**
52253      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52254      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52255      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52256      */
52257     autoSizeColumns : false,
52258
52259     /**
52260      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52261      */
52262     autoSizeHeaders : true,
52263
52264     /**
52265      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52266      */
52267     monitorWindowResize : true,
52268
52269     /**
52270      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52271      * rows measured to get a columns size. Default is 0 (all rows).
52272      */
52273     maxRowsToMeasure : 0,
52274
52275     /**
52276      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52277      */
52278     trackMouseOver : true,
52279
52280     /**
52281     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52282     */
52283     
52284     /**
52285     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52286     */
52287     enableDragDrop : false,
52288     
52289     /**
52290     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52291     */
52292     enableColumnMove : true,
52293     
52294     /**
52295     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52296     */
52297     enableColumnHide : true,
52298     
52299     /**
52300     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52301     */
52302     enableRowHeightSync : false,
52303     
52304     /**
52305     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52306     */
52307     stripeRows : true,
52308     
52309     /**
52310     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52311     */
52312     autoHeight : false,
52313
52314     /**
52315      * @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.
52316      */
52317     autoExpandColumn : false,
52318
52319     /**
52320     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52321     * Default is 50.
52322     */
52323     autoExpandMin : 50,
52324
52325     /**
52326     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52327     */
52328     autoExpandMax : 1000,
52329
52330     /**
52331     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52332     */
52333     view : null,
52334
52335     /**
52336     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52337     */
52338     loadMask : false,
52339     /**
52340     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52341     */
52342     dropTarget: false,
52343     
52344    
52345     
52346     // private
52347     rendered : false,
52348
52349     /**
52350     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52351     * of a fixed width. Default is false.
52352     */
52353     /**
52354     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52355     */
52356     /**
52357      * Called once after all setup has been completed and the grid is ready to be rendered.
52358      * @return {Roo.grid.Grid} this
52359      */
52360     render : function()
52361     {
52362         var c = this.container;
52363         // try to detect autoHeight/width mode
52364         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52365             this.autoHeight = true;
52366         }
52367         var view = this.getView();
52368         view.init(this);
52369
52370         c.on("click", this.onClick, this);
52371         c.on("dblclick", this.onDblClick, this);
52372         c.on("contextmenu", this.onContextMenu, this);
52373         c.on("keydown", this.onKeyDown, this);
52374         if (Roo.isTouch) {
52375             c.on("touchstart", this.onTouchStart, this);
52376         }
52377
52378         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52379
52380         this.getSelectionModel().init(this);
52381
52382         view.render();
52383
52384         if(this.loadMask){
52385             this.loadMask = new Roo.LoadMask(this.container,
52386                     Roo.apply({store:this.dataSource}, this.loadMask));
52387         }
52388         
52389         
52390         if (this.toolbar && this.toolbar.xtype) {
52391             this.toolbar.container = this.getView().getHeaderPanel(true);
52392             this.toolbar = new Roo.Toolbar(this.toolbar);
52393         }
52394         if (this.footer && this.footer.xtype) {
52395             this.footer.dataSource = this.getDataSource();
52396             this.footer.container = this.getView().getFooterPanel(true);
52397             this.footer = Roo.factory(this.footer, Roo);
52398         }
52399         if (this.dropTarget && this.dropTarget.xtype) {
52400             delete this.dropTarget.xtype;
52401             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52402         }
52403         
52404         
52405         this.rendered = true;
52406         this.fireEvent('render', this);
52407         return this;
52408     },
52409
52410         /**
52411          * Reconfigures the grid to use a different Store and Column Model.
52412          * The View will be bound to the new objects and refreshed.
52413          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52414          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52415          */
52416     reconfigure : function(dataSource, colModel){
52417         if(this.loadMask){
52418             this.loadMask.destroy();
52419             this.loadMask = new Roo.LoadMask(this.container,
52420                     Roo.apply({store:dataSource}, this.loadMask));
52421         }
52422         this.view.bind(dataSource, colModel);
52423         this.dataSource = dataSource;
52424         this.colModel = colModel;
52425         this.view.refresh(true);
52426     },
52427
52428     // private
52429     onKeyDown : function(e){
52430         this.fireEvent("keydown", e);
52431     },
52432
52433     /**
52434      * Destroy this grid.
52435      * @param {Boolean} removeEl True to remove the element
52436      */
52437     destroy : function(removeEl, keepListeners){
52438         if(this.loadMask){
52439             this.loadMask.destroy();
52440         }
52441         var c = this.container;
52442         c.removeAllListeners();
52443         this.view.destroy();
52444         this.colModel.purgeListeners();
52445         if(!keepListeners){
52446             this.purgeListeners();
52447         }
52448         c.update("");
52449         if(removeEl === true){
52450             c.remove();
52451         }
52452     },
52453
52454     // private
52455     processEvent : function(name, e){
52456         // does this fire select???
52457         Roo.log('grid:processEvent '  + name);
52458         
52459         if (name != 'touchstart' ) {
52460             this.fireEvent(name, e);    
52461         }
52462         
52463         var t = e.getTarget();
52464         var v = this.view;
52465         var header = v.findHeaderIndex(t);
52466         if(header !== false){
52467             var ename = name == 'touchstart' ? 'click' : name;
52468              
52469             this.fireEvent("header" + ename, this, header, e);
52470         }else{
52471             var row = v.findRowIndex(t);
52472             var cell = v.findCellIndex(t);
52473             if (name == 'touchstart') {
52474                 // first touch is always a click.
52475                 // hopefull this happens after selection is updated.?
52476                 name = false;
52477                 
52478                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52479                     var cs = this.selModel.getSelectedCell();
52480                     if (row == cs[0] && cell == cs[1]){
52481                         name = 'dblclick';
52482                     }
52483                 }
52484                 if (typeof(this.selModel.getSelections) != 'undefined') {
52485                     var cs = this.selModel.getSelections();
52486                     var ds = this.dataSource;
52487                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52488                         name = 'dblclick';
52489                     }
52490                 }
52491                 if (!name) {
52492                     return;
52493                 }
52494             }
52495             
52496             
52497             if(row !== false){
52498                 this.fireEvent("row" + name, this, row, e);
52499                 if(cell !== false){
52500                     this.fireEvent("cell" + name, this, row, cell, e);
52501                 }
52502             }
52503         }
52504     },
52505
52506     // private
52507     onClick : function(e){
52508         this.processEvent("click", e);
52509     },
52510    // private
52511     onTouchStart : function(e){
52512         this.processEvent("touchstart", e);
52513     },
52514
52515     // private
52516     onContextMenu : function(e, t){
52517         this.processEvent("contextmenu", e);
52518     },
52519
52520     // private
52521     onDblClick : function(e){
52522         this.processEvent("dblclick", e);
52523     },
52524
52525     // private
52526     walkCells : function(row, col, step, fn, scope){
52527         var cm = this.colModel, clen = cm.getColumnCount();
52528         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52529         if(step < 0){
52530             if(col < 0){
52531                 row--;
52532                 first = false;
52533             }
52534             while(row >= 0){
52535                 if(!first){
52536                     col = clen-1;
52537                 }
52538                 first = false;
52539                 while(col >= 0){
52540                     if(fn.call(scope || this, row, col, cm) === true){
52541                         return [row, col];
52542                     }
52543                     col--;
52544                 }
52545                 row--;
52546             }
52547         } else {
52548             if(col >= clen){
52549                 row++;
52550                 first = false;
52551             }
52552             while(row < rlen){
52553                 if(!first){
52554                     col = 0;
52555                 }
52556                 first = false;
52557                 while(col < clen){
52558                     if(fn.call(scope || this, row, col, cm) === true){
52559                         return [row, col];
52560                     }
52561                     col++;
52562                 }
52563                 row++;
52564             }
52565         }
52566         return null;
52567     },
52568
52569     // private
52570     getSelections : function(){
52571         return this.selModel.getSelections();
52572     },
52573
52574     /**
52575      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52576      * but if manual update is required this method will initiate it.
52577      */
52578     autoSize : function(){
52579         if(this.rendered){
52580             this.view.layout();
52581             if(this.view.adjustForScroll){
52582                 this.view.adjustForScroll();
52583             }
52584         }
52585     },
52586
52587     /**
52588      * Returns the grid's underlying element.
52589      * @return {Element} The element
52590      */
52591     getGridEl : function(){
52592         return this.container;
52593     },
52594
52595     // private for compatibility, overridden by editor grid
52596     stopEditing : function(){},
52597
52598     /**
52599      * Returns the grid's SelectionModel.
52600      * @return {SelectionModel}
52601      */
52602     getSelectionModel : function(){
52603         if(!this.selModel){
52604             this.selModel = new Roo.grid.RowSelectionModel();
52605         }
52606         return this.selModel;
52607     },
52608
52609     /**
52610      * Returns the grid's DataSource.
52611      * @return {DataSource}
52612      */
52613     getDataSource : function(){
52614         return this.dataSource;
52615     },
52616
52617     /**
52618      * Returns the grid's ColumnModel.
52619      * @return {ColumnModel}
52620      */
52621     getColumnModel : function(){
52622         return this.colModel;
52623     },
52624
52625     /**
52626      * Returns the grid's GridView object.
52627      * @return {GridView}
52628      */
52629     getView : function(){
52630         if(!this.view){
52631             this.view = new Roo.grid.GridView(this.viewConfig);
52632         }
52633         return this.view;
52634     },
52635     /**
52636      * Called to get grid's drag proxy text, by default returns this.ddText.
52637      * @return {String}
52638      */
52639     getDragDropText : function(){
52640         var count = this.selModel.getCount();
52641         return String.format(this.ddText, count, count == 1 ? '' : 's');
52642     }
52643 });
52644 /**
52645  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52646  * %0 is replaced with the number of selected rows.
52647  * @type String
52648  */
52649 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52650  * Based on:
52651  * Ext JS Library 1.1.1
52652  * Copyright(c) 2006-2007, Ext JS, LLC.
52653  *
52654  * Originally Released Under LGPL - original licence link has changed is not relivant.
52655  *
52656  * Fork - LGPL
52657  * <script type="text/javascript">
52658  */
52659  
52660 Roo.grid.AbstractGridView = function(){
52661         this.grid = null;
52662         
52663         this.events = {
52664             "beforerowremoved" : true,
52665             "beforerowsinserted" : true,
52666             "beforerefresh" : true,
52667             "rowremoved" : true,
52668             "rowsinserted" : true,
52669             "rowupdated" : true,
52670             "refresh" : true
52671         };
52672     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52673 };
52674
52675 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52676     rowClass : "x-grid-row",
52677     cellClass : "x-grid-cell",
52678     tdClass : "x-grid-td",
52679     hdClass : "x-grid-hd",
52680     splitClass : "x-grid-hd-split",
52681     
52682     init: function(grid){
52683         this.grid = grid;
52684                 var cid = this.grid.getGridEl().id;
52685         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52686         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52687         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52688         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52689         },
52690         
52691     getColumnRenderers : function(){
52692         var renderers = [];
52693         var cm = this.grid.colModel;
52694         var colCount = cm.getColumnCount();
52695         for(var i = 0; i < colCount; i++){
52696             renderers[i] = cm.getRenderer(i);
52697         }
52698         return renderers;
52699     },
52700     
52701     getColumnIds : function(){
52702         var ids = [];
52703         var cm = this.grid.colModel;
52704         var colCount = cm.getColumnCount();
52705         for(var i = 0; i < colCount; i++){
52706             ids[i] = cm.getColumnId(i);
52707         }
52708         return ids;
52709     },
52710     
52711     getDataIndexes : function(){
52712         if(!this.indexMap){
52713             this.indexMap = this.buildIndexMap();
52714         }
52715         return this.indexMap.colToData;
52716     },
52717     
52718     getColumnIndexByDataIndex : function(dataIndex){
52719         if(!this.indexMap){
52720             this.indexMap = this.buildIndexMap();
52721         }
52722         return this.indexMap.dataToCol[dataIndex];
52723     },
52724     
52725     /**
52726      * Set a css style for a column dynamically. 
52727      * @param {Number} colIndex The index of the column
52728      * @param {String} name The css property name
52729      * @param {String} value The css value
52730      */
52731     setCSSStyle : function(colIndex, name, value){
52732         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52733         Roo.util.CSS.updateRule(selector, name, value);
52734     },
52735     
52736     generateRules : function(cm){
52737         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52738         Roo.util.CSS.removeStyleSheet(rulesId);
52739         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52740             var cid = cm.getColumnId(i);
52741             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52742                          this.tdSelector, cid, " {\n}\n",
52743                          this.hdSelector, cid, " {\n}\n",
52744                          this.splitSelector, cid, " {\n}\n");
52745         }
52746         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52747     }
52748 });/*
52749  * Based on:
52750  * Ext JS Library 1.1.1
52751  * Copyright(c) 2006-2007, Ext JS, LLC.
52752  *
52753  * Originally Released Under LGPL - original licence link has changed is not relivant.
52754  *
52755  * Fork - LGPL
52756  * <script type="text/javascript">
52757  */
52758
52759 // private
52760 // This is a support class used internally by the Grid components
52761 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52762     this.grid = grid;
52763     this.view = grid.getView();
52764     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52765     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52766     if(hd2){
52767         this.setHandleElId(Roo.id(hd));
52768         this.setOuterHandleElId(Roo.id(hd2));
52769     }
52770     this.scroll = false;
52771 };
52772 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52773     maxDragWidth: 120,
52774     getDragData : function(e){
52775         var t = Roo.lib.Event.getTarget(e);
52776         var h = this.view.findHeaderCell(t);
52777         if(h){
52778             return {ddel: h.firstChild, header:h};
52779         }
52780         return false;
52781     },
52782
52783     onInitDrag : function(e){
52784         this.view.headersDisabled = true;
52785         var clone = this.dragData.ddel.cloneNode(true);
52786         clone.id = Roo.id();
52787         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52788         this.proxy.update(clone);
52789         return true;
52790     },
52791
52792     afterValidDrop : function(){
52793         var v = this.view;
52794         setTimeout(function(){
52795             v.headersDisabled = false;
52796         }, 50);
52797     },
52798
52799     afterInvalidDrop : function(){
52800         var v = this.view;
52801         setTimeout(function(){
52802             v.headersDisabled = false;
52803         }, 50);
52804     }
52805 });
52806 /*
52807  * Based on:
52808  * Ext JS Library 1.1.1
52809  * Copyright(c) 2006-2007, Ext JS, LLC.
52810  *
52811  * Originally Released Under LGPL - original licence link has changed is not relivant.
52812  *
52813  * Fork - LGPL
52814  * <script type="text/javascript">
52815  */
52816 // private
52817 // This is a support class used internally by the Grid components
52818 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52819     this.grid = grid;
52820     this.view = grid.getView();
52821     // split the proxies so they don't interfere with mouse events
52822     this.proxyTop = Roo.DomHelper.append(document.body, {
52823         cls:"col-move-top", html:"&#160;"
52824     }, true);
52825     this.proxyBottom = Roo.DomHelper.append(document.body, {
52826         cls:"col-move-bottom", html:"&#160;"
52827     }, true);
52828     this.proxyTop.hide = this.proxyBottom.hide = function(){
52829         this.setLeftTop(-100,-100);
52830         this.setStyle("visibility", "hidden");
52831     };
52832     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52833     // temporarily disabled
52834     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52835     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52836 };
52837 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52838     proxyOffsets : [-4, -9],
52839     fly: Roo.Element.fly,
52840
52841     getTargetFromEvent : function(e){
52842         var t = Roo.lib.Event.getTarget(e);
52843         var cindex = this.view.findCellIndex(t);
52844         if(cindex !== false){
52845             return this.view.getHeaderCell(cindex);
52846         }
52847         return null;
52848     },
52849
52850     nextVisible : function(h){
52851         var v = this.view, cm = this.grid.colModel;
52852         h = h.nextSibling;
52853         while(h){
52854             if(!cm.isHidden(v.getCellIndex(h))){
52855                 return h;
52856             }
52857             h = h.nextSibling;
52858         }
52859         return null;
52860     },
52861
52862     prevVisible : function(h){
52863         var v = this.view, cm = this.grid.colModel;
52864         h = h.prevSibling;
52865         while(h){
52866             if(!cm.isHidden(v.getCellIndex(h))){
52867                 return h;
52868             }
52869             h = h.prevSibling;
52870         }
52871         return null;
52872     },
52873
52874     positionIndicator : function(h, n, e){
52875         var x = Roo.lib.Event.getPageX(e);
52876         var r = Roo.lib.Dom.getRegion(n.firstChild);
52877         var px, pt, py = r.top + this.proxyOffsets[1];
52878         if((r.right - x) <= (r.right-r.left)/2){
52879             px = r.right+this.view.borderWidth;
52880             pt = "after";
52881         }else{
52882             px = r.left;
52883             pt = "before";
52884         }
52885         var oldIndex = this.view.getCellIndex(h);
52886         var newIndex = this.view.getCellIndex(n);
52887
52888         if(this.grid.colModel.isFixed(newIndex)){
52889             return false;
52890         }
52891
52892         var locked = this.grid.colModel.isLocked(newIndex);
52893
52894         if(pt == "after"){
52895             newIndex++;
52896         }
52897         if(oldIndex < newIndex){
52898             newIndex--;
52899         }
52900         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52901             return false;
52902         }
52903         px +=  this.proxyOffsets[0];
52904         this.proxyTop.setLeftTop(px, py);
52905         this.proxyTop.show();
52906         if(!this.bottomOffset){
52907             this.bottomOffset = this.view.mainHd.getHeight();
52908         }
52909         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52910         this.proxyBottom.show();
52911         return pt;
52912     },
52913
52914     onNodeEnter : function(n, dd, e, data){
52915         if(data.header != n){
52916             this.positionIndicator(data.header, n, e);
52917         }
52918     },
52919
52920     onNodeOver : function(n, dd, e, data){
52921         var result = false;
52922         if(data.header != n){
52923             result = this.positionIndicator(data.header, n, e);
52924         }
52925         if(!result){
52926             this.proxyTop.hide();
52927             this.proxyBottom.hide();
52928         }
52929         return result ? this.dropAllowed : this.dropNotAllowed;
52930     },
52931
52932     onNodeOut : function(n, dd, e, data){
52933         this.proxyTop.hide();
52934         this.proxyBottom.hide();
52935     },
52936
52937     onNodeDrop : function(n, dd, e, data){
52938         var h = data.header;
52939         if(h != n){
52940             var cm = this.grid.colModel;
52941             var x = Roo.lib.Event.getPageX(e);
52942             var r = Roo.lib.Dom.getRegion(n.firstChild);
52943             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52944             var oldIndex = this.view.getCellIndex(h);
52945             var newIndex = this.view.getCellIndex(n);
52946             var locked = cm.isLocked(newIndex);
52947             if(pt == "after"){
52948                 newIndex++;
52949             }
52950             if(oldIndex < newIndex){
52951                 newIndex--;
52952             }
52953             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52954                 return false;
52955             }
52956             cm.setLocked(oldIndex, locked, true);
52957             cm.moveColumn(oldIndex, newIndex);
52958             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52959             return true;
52960         }
52961         return false;
52962     }
52963 });
52964 /*
52965  * Based on:
52966  * Ext JS Library 1.1.1
52967  * Copyright(c) 2006-2007, Ext JS, LLC.
52968  *
52969  * Originally Released Under LGPL - original licence link has changed is not relivant.
52970  *
52971  * Fork - LGPL
52972  * <script type="text/javascript">
52973  */
52974   
52975 /**
52976  * @class Roo.grid.GridView
52977  * @extends Roo.util.Observable
52978  *
52979  * @constructor
52980  * @param {Object} config
52981  */
52982 Roo.grid.GridView = function(config){
52983     Roo.grid.GridView.superclass.constructor.call(this);
52984     this.el = null;
52985
52986     Roo.apply(this, config);
52987 };
52988
52989 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52990
52991     unselectable :  'unselectable="on"',
52992     unselectableCls :  'x-unselectable',
52993     
52994     
52995     rowClass : "x-grid-row",
52996
52997     cellClass : "x-grid-col",
52998
52999     tdClass : "x-grid-td",
53000
53001     hdClass : "x-grid-hd",
53002
53003     splitClass : "x-grid-split",
53004
53005     sortClasses : ["sort-asc", "sort-desc"],
53006
53007     enableMoveAnim : false,
53008
53009     hlColor: "C3DAF9",
53010
53011     dh : Roo.DomHelper,
53012
53013     fly : Roo.Element.fly,
53014
53015     css : Roo.util.CSS,
53016
53017     borderWidth: 1,
53018
53019     splitOffset: 3,
53020
53021     scrollIncrement : 22,
53022
53023     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53024
53025     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53026
53027     bind : function(ds, cm){
53028         if(this.ds){
53029             this.ds.un("load", this.onLoad, this);
53030             this.ds.un("datachanged", this.onDataChange, this);
53031             this.ds.un("add", this.onAdd, this);
53032             this.ds.un("remove", this.onRemove, this);
53033             this.ds.un("update", this.onUpdate, this);
53034             this.ds.un("clear", this.onClear, this);
53035         }
53036         if(ds){
53037             ds.on("load", this.onLoad, this);
53038             ds.on("datachanged", this.onDataChange, this);
53039             ds.on("add", this.onAdd, this);
53040             ds.on("remove", this.onRemove, this);
53041             ds.on("update", this.onUpdate, this);
53042             ds.on("clear", this.onClear, this);
53043         }
53044         this.ds = ds;
53045
53046         if(this.cm){
53047             this.cm.un("widthchange", this.onColWidthChange, this);
53048             this.cm.un("headerchange", this.onHeaderChange, this);
53049             this.cm.un("hiddenchange", this.onHiddenChange, this);
53050             this.cm.un("columnmoved", this.onColumnMove, this);
53051             this.cm.un("columnlockchange", this.onColumnLock, this);
53052         }
53053         if(cm){
53054             this.generateRules(cm);
53055             cm.on("widthchange", this.onColWidthChange, this);
53056             cm.on("headerchange", this.onHeaderChange, this);
53057             cm.on("hiddenchange", this.onHiddenChange, this);
53058             cm.on("columnmoved", this.onColumnMove, this);
53059             cm.on("columnlockchange", this.onColumnLock, this);
53060         }
53061         this.cm = cm;
53062     },
53063
53064     init: function(grid){
53065         Roo.grid.GridView.superclass.init.call(this, grid);
53066
53067         this.bind(grid.dataSource, grid.colModel);
53068
53069         grid.on("headerclick", this.handleHeaderClick, this);
53070
53071         if(grid.trackMouseOver){
53072             grid.on("mouseover", this.onRowOver, this);
53073             grid.on("mouseout", this.onRowOut, this);
53074         }
53075         grid.cancelTextSelection = function(){};
53076         this.gridId = grid.id;
53077
53078         var tpls = this.templates || {};
53079
53080         if(!tpls.master){
53081             tpls.master = new Roo.Template(
53082                '<div class="x-grid" hidefocus="true">',
53083                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53084                   '<div class="x-grid-topbar"></div>',
53085                   '<div class="x-grid-scroller"><div></div></div>',
53086                   '<div class="x-grid-locked">',
53087                       '<div class="x-grid-header">{lockedHeader}</div>',
53088                       '<div class="x-grid-body">{lockedBody}</div>',
53089                   "</div>",
53090                   '<div class="x-grid-viewport">',
53091                       '<div class="x-grid-header">{header}</div>',
53092                       '<div class="x-grid-body">{body}</div>',
53093                   "</div>",
53094                   '<div class="x-grid-bottombar"></div>',
53095                  
53096                   '<div class="x-grid-resize-proxy">&#160;</div>',
53097                "</div>"
53098             );
53099             tpls.master.disableformats = true;
53100         }
53101
53102         if(!tpls.header){
53103             tpls.header = new Roo.Template(
53104                '<table border="0" cellspacing="0" cellpadding="0">',
53105                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53106                "</table>{splits}"
53107             );
53108             tpls.header.disableformats = true;
53109         }
53110         tpls.header.compile();
53111
53112         if(!tpls.hcell){
53113             tpls.hcell = new Roo.Template(
53114                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53115                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53116                 "</div></td>"
53117              );
53118              tpls.hcell.disableFormats = true;
53119         }
53120         tpls.hcell.compile();
53121
53122         if(!tpls.hsplit){
53123             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53124                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53125             tpls.hsplit.disableFormats = true;
53126         }
53127         tpls.hsplit.compile();
53128
53129         if(!tpls.body){
53130             tpls.body = new Roo.Template(
53131                '<table border="0" cellspacing="0" cellpadding="0">',
53132                "<tbody>{rows}</tbody>",
53133                "</table>"
53134             );
53135             tpls.body.disableFormats = true;
53136         }
53137         tpls.body.compile();
53138
53139         if(!tpls.row){
53140             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53141             tpls.row.disableFormats = true;
53142         }
53143         tpls.row.compile();
53144
53145         if(!tpls.cell){
53146             tpls.cell = new Roo.Template(
53147                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53148                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53149                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53150                 "</td>"
53151             );
53152             tpls.cell.disableFormats = true;
53153         }
53154         tpls.cell.compile();
53155
53156         this.templates = tpls;
53157     },
53158
53159     // remap these for backwards compat
53160     onColWidthChange : function(){
53161         this.updateColumns.apply(this, arguments);
53162     },
53163     onHeaderChange : function(){
53164         this.updateHeaders.apply(this, arguments);
53165     }, 
53166     onHiddenChange : function(){
53167         this.handleHiddenChange.apply(this, arguments);
53168     },
53169     onColumnMove : function(){
53170         this.handleColumnMove.apply(this, arguments);
53171     },
53172     onColumnLock : function(){
53173         this.handleLockChange.apply(this, arguments);
53174     },
53175
53176     onDataChange : function(){
53177         this.refresh();
53178         this.updateHeaderSortState();
53179     },
53180
53181     onClear : function(){
53182         this.refresh();
53183     },
53184
53185     onUpdate : function(ds, record){
53186         this.refreshRow(record);
53187     },
53188
53189     refreshRow : function(record){
53190         var ds = this.ds, index;
53191         if(typeof record == 'number'){
53192             index = record;
53193             record = ds.getAt(index);
53194         }else{
53195             index = ds.indexOf(record);
53196         }
53197         this.insertRows(ds, index, index, true);
53198         this.onRemove(ds, record, index+1, true);
53199         this.syncRowHeights(index, index);
53200         this.layout();
53201         this.fireEvent("rowupdated", this, index, record);
53202     },
53203
53204     onAdd : function(ds, records, index){
53205         this.insertRows(ds, index, index + (records.length-1));
53206     },
53207
53208     onRemove : function(ds, record, index, isUpdate){
53209         if(isUpdate !== true){
53210             this.fireEvent("beforerowremoved", this, index, record);
53211         }
53212         var bt = this.getBodyTable(), lt = this.getLockedTable();
53213         if(bt.rows[index]){
53214             bt.firstChild.removeChild(bt.rows[index]);
53215         }
53216         if(lt.rows[index]){
53217             lt.firstChild.removeChild(lt.rows[index]);
53218         }
53219         if(isUpdate !== true){
53220             this.stripeRows(index);
53221             this.syncRowHeights(index, index);
53222             this.layout();
53223             this.fireEvent("rowremoved", this, index, record);
53224         }
53225     },
53226
53227     onLoad : function(){
53228         this.scrollToTop();
53229     },
53230
53231     /**
53232      * Scrolls the grid to the top
53233      */
53234     scrollToTop : function(){
53235         if(this.scroller){
53236             this.scroller.dom.scrollTop = 0;
53237             this.syncScroll();
53238         }
53239     },
53240
53241     /**
53242      * Gets a panel in the header of the grid that can be used for toolbars etc.
53243      * After modifying the contents of this panel a call to grid.autoSize() may be
53244      * required to register any changes in size.
53245      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53246      * @return Roo.Element
53247      */
53248     getHeaderPanel : function(doShow){
53249         if(doShow){
53250             this.headerPanel.show();
53251         }
53252         return this.headerPanel;
53253     },
53254
53255     /**
53256      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53257      * After modifying the contents of this panel a call to grid.autoSize() may be
53258      * required to register any changes in size.
53259      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53260      * @return Roo.Element
53261      */
53262     getFooterPanel : function(doShow){
53263         if(doShow){
53264             this.footerPanel.show();
53265         }
53266         return this.footerPanel;
53267     },
53268
53269     initElements : function(){
53270         var E = Roo.Element;
53271         var el = this.grid.getGridEl().dom.firstChild;
53272         var cs = el.childNodes;
53273
53274         this.el = new E(el);
53275         
53276          this.focusEl = new E(el.firstChild);
53277         this.focusEl.swallowEvent("click", true);
53278         
53279         this.headerPanel = new E(cs[1]);
53280         this.headerPanel.enableDisplayMode("block");
53281
53282         this.scroller = new E(cs[2]);
53283         this.scrollSizer = new E(this.scroller.dom.firstChild);
53284
53285         this.lockedWrap = new E(cs[3]);
53286         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53287         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53288
53289         this.mainWrap = new E(cs[4]);
53290         this.mainHd = new E(this.mainWrap.dom.firstChild);
53291         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53292
53293         this.footerPanel = new E(cs[5]);
53294         this.footerPanel.enableDisplayMode("block");
53295
53296         this.resizeProxy = new E(cs[6]);
53297
53298         this.headerSelector = String.format(
53299            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53300            this.lockedHd.id, this.mainHd.id
53301         );
53302
53303         this.splitterSelector = String.format(
53304            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53305            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53306         );
53307     },
53308     idToCssName : function(s)
53309     {
53310         return s.replace(/[^a-z0-9]+/ig, '-');
53311     },
53312
53313     getHeaderCell : function(index){
53314         return Roo.DomQuery.select(this.headerSelector)[index];
53315     },
53316
53317     getHeaderCellMeasure : function(index){
53318         return this.getHeaderCell(index).firstChild;
53319     },
53320
53321     getHeaderCellText : function(index){
53322         return this.getHeaderCell(index).firstChild.firstChild;
53323     },
53324
53325     getLockedTable : function(){
53326         return this.lockedBody.dom.firstChild;
53327     },
53328
53329     getBodyTable : function(){
53330         return this.mainBody.dom.firstChild;
53331     },
53332
53333     getLockedRow : function(index){
53334         return this.getLockedTable().rows[index];
53335     },
53336
53337     getRow : function(index){
53338         return this.getBodyTable().rows[index];
53339     },
53340
53341     getRowComposite : function(index){
53342         if(!this.rowEl){
53343             this.rowEl = new Roo.CompositeElementLite();
53344         }
53345         var els = [], lrow, mrow;
53346         if(lrow = this.getLockedRow(index)){
53347             els.push(lrow);
53348         }
53349         if(mrow = this.getRow(index)){
53350             els.push(mrow);
53351         }
53352         this.rowEl.elements = els;
53353         return this.rowEl;
53354     },
53355     /**
53356      * Gets the 'td' of the cell
53357      * 
53358      * @param {Integer} rowIndex row to select
53359      * @param {Integer} colIndex column to select
53360      * 
53361      * @return {Object} 
53362      */
53363     getCell : function(rowIndex, colIndex){
53364         var locked = this.cm.getLockedCount();
53365         var source;
53366         if(colIndex < locked){
53367             source = this.lockedBody.dom.firstChild;
53368         }else{
53369             source = this.mainBody.dom.firstChild;
53370             colIndex -= locked;
53371         }
53372         return source.rows[rowIndex].childNodes[colIndex];
53373     },
53374
53375     getCellText : function(rowIndex, colIndex){
53376         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53377     },
53378
53379     getCellBox : function(cell){
53380         var b = this.fly(cell).getBox();
53381         if(Roo.isOpera){ // opera fails to report the Y
53382             b.y = cell.offsetTop + this.mainBody.getY();
53383         }
53384         return b;
53385     },
53386
53387     getCellIndex : function(cell){
53388         var id = String(cell.className).match(this.cellRE);
53389         if(id){
53390             return parseInt(id[1], 10);
53391         }
53392         return 0;
53393     },
53394
53395     findHeaderIndex : function(n){
53396         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53397         return r ? this.getCellIndex(r) : false;
53398     },
53399
53400     findHeaderCell : function(n){
53401         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53402         return r ? r : false;
53403     },
53404
53405     findRowIndex : function(n){
53406         if(!n){
53407             return false;
53408         }
53409         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53410         return r ? r.rowIndex : false;
53411     },
53412
53413     findCellIndex : function(node){
53414         var stop = this.el.dom;
53415         while(node && node != stop){
53416             if(this.findRE.test(node.className)){
53417                 return this.getCellIndex(node);
53418             }
53419             node = node.parentNode;
53420         }
53421         return false;
53422     },
53423
53424     getColumnId : function(index){
53425         return this.cm.getColumnId(index);
53426     },
53427
53428     getSplitters : function()
53429     {
53430         if(this.splitterSelector){
53431            return Roo.DomQuery.select(this.splitterSelector);
53432         }else{
53433             return null;
53434       }
53435     },
53436
53437     getSplitter : function(index){
53438         return this.getSplitters()[index];
53439     },
53440
53441     onRowOver : function(e, t){
53442         var row;
53443         if((row = this.findRowIndex(t)) !== false){
53444             this.getRowComposite(row).addClass("x-grid-row-over");
53445         }
53446     },
53447
53448     onRowOut : function(e, t){
53449         var row;
53450         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53451             this.getRowComposite(row).removeClass("x-grid-row-over");
53452         }
53453     },
53454
53455     renderHeaders : function(){
53456         var cm = this.cm;
53457         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53458         var cb = [], lb = [], sb = [], lsb = [], p = {};
53459         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53460             p.cellId = "x-grid-hd-0-" + i;
53461             p.splitId = "x-grid-csplit-0-" + i;
53462             p.id = cm.getColumnId(i);
53463             p.title = cm.getColumnTooltip(i) || "";
53464             p.value = cm.getColumnHeader(i) || "";
53465             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53466             if(!cm.isLocked(i)){
53467                 cb[cb.length] = ct.apply(p);
53468                 sb[sb.length] = st.apply(p);
53469             }else{
53470                 lb[lb.length] = ct.apply(p);
53471                 lsb[lsb.length] = st.apply(p);
53472             }
53473         }
53474         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53475                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53476     },
53477
53478     updateHeaders : function(){
53479         var html = this.renderHeaders();
53480         this.lockedHd.update(html[0]);
53481         this.mainHd.update(html[1]);
53482     },
53483
53484     /**
53485      * Focuses the specified row.
53486      * @param {Number} row The row index
53487      */
53488     focusRow : function(row)
53489     {
53490         //Roo.log('GridView.focusRow');
53491         var x = this.scroller.dom.scrollLeft;
53492         this.focusCell(row, 0, false);
53493         this.scroller.dom.scrollLeft = x;
53494     },
53495
53496     /**
53497      * Focuses the specified cell.
53498      * @param {Number} row The row index
53499      * @param {Number} col The column index
53500      * @param {Boolean} hscroll false to disable horizontal scrolling
53501      */
53502     focusCell : function(row, col, hscroll)
53503     {
53504         //Roo.log('GridView.focusCell');
53505         var el = this.ensureVisible(row, col, hscroll);
53506         this.focusEl.alignTo(el, "tl-tl");
53507         if(Roo.isGecko){
53508             this.focusEl.focus();
53509         }else{
53510             this.focusEl.focus.defer(1, this.focusEl);
53511         }
53512     },
53513
53514     /**
53515      * Scrolls the specified cell into view
53516      * @param {Number} row The row index
53517      * @param {Number} col The column index
53518      * @param {Boolean} hscroll false to disable horizontal scrolling
53519      */
53520     ensureVisible : function(row, col, hscroll)
53521     {
53522         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53523         //return null; //disable for testing.
53524         if(typeof row != "number"){
53525             row = row.rowIndex;
53526         }
53527         if(row < 0 && row >= this.ds.getCount()){
53528             return  null;
53529         }
53530         col = (col !== undefined ? col : 0);
53531         var cm = this.grid.colModel;
53532         while(cm.isHidden(col)){
53533             col++;
53534         }
53535
53536         var el = this.getCell(row, col);
53537         if(!el){
53538             return null;
53539         }
53540         var c = this.scroller.dom;
53541
53542         var ctop = parseInt(el.offsetTop, 10);
53543         var cleft = parseInt(el.offsetLeft, 10);
53544         var cbot = ctop + el.offsetHeight;
53545         var cright = cleft + el.offsetWidth;
53546         
53547         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53548         var stop = parseInt(c.scrollTop, 10);
53549         var sleft = parseInt(c.scrollLeft, 10);
53550         var sbot = stop + ch;
53551         var sright = sleft + c.clientWidth;
53552         /*
53553         Roo.log('GridView.ensureVisible:' +
53554                 ' ctop:' + ctop +
53555                 ' c.clientHeight:' + c.clientHeight +
53556                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53557                 ' stop:' + stop +
53558                 ' cbot:' + cbot +
53559                 ' sbot:' + sbot +
53560                 ' ch:' + ch  
53561                 );
53562         */
53563         if(ctop < stop){
53564              c.scrollTop = ctop;
53565             //Roo.log("set scrolltop to ctop DISABLE?");
53566         }else if(cbot > sbot){
53567             //Roo.log("set scrolltop to cbot-ch");
53568             c.scrollTop = cbot-ch;
53569         }
53570         
53571         if(hscroll !== false){
53572             if(cleft < sleft){
53573                 c.scrollLeft = cleft;
53574             }else if(cright > sright){
53575                 c.scrollLeft = cright-c.clientWidth;
53576             }
53577         }
53578          
53579         return el;
53580     },
53581
53582     updateColumns : function(){
53583         this.grid.stopEditing();
53584         var cm = this.grid.colModel, colIds = this.getColumnIds();
53585         //var totalWidth = cm.getTotalWidth();
53586         var pos = 0;
53587         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53588             //if(cm.isHidden(i)) continue;
53589             var w = cm.getColumnWidth(i);
53590             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53591             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53592         }
53593         this.updateSplitters();
53594     },
53595
53596     generateRules : function(cm){
53597         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53598         Roo.util.CSS.removeStyleSheet(rulesId);
53599         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53600             var cid = cm.getColumnId(i);
53601             var align = '';
53602             if(cm.config[i].align){
53603                 align = 'text-align:'+cm.config[i].align+';';
53604             }
53605             var hidden = '';
53606             if(cm.isHidden(i)){
53607                 hidden = 'display:none;';
53608             }
53609             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53610             ruleBuf.push(
53611                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53612                     this.hdSelector, cid, " {\n", align, width, "}\n",
53613                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53614                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53615         }
53616         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53617     },
53618
53619     updateSplitters : function(){
53620         var cm = this.cm, s = this.getSplitters();
53621         if(s){ // splitters not created yet
53622             var pos = 0, locked = true;
53623             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53624                 if(cm.isHidden(i)) continue;
53625                 var w = cm.getColumnWidth(i); // make sure it's a number
53626                 if(!cm.isLocked(i) && locked){
53627                     pos = 0;
53628                     locked = false;
53629                 }
53630                 pos += w;
53631                 s[i].style.left = (pos-this.splitOffset) + "px";
53632             }
53633         }
53634     },
53635
53636     handleHiddenChange : function(colModel, colIndex, hidden){
53637         if(hidden){
53638             this.hideColumn(colIndex);
53639         }else{
53640             this.unhideColumn(colIndex);
53641         }
53642     },
53643
53644     hideColumn : function(colIndex){
53645         var cid = this.getColumnId(colIndex);
53646         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53647         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53648         if(Roo.isSafari){
53649             this.updateHeaders();
53650         }
53651         this.updateSplitters();
53652         this.layout();
53653     },
53654
53655     unhideColumn : function(colIndex){
53656         var cid = this.getColumnId(colIndex);
53657         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53658         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53659
53660         if(Roo.isSafari){
53661             this.updateHeaders();
53662         }
53663         this.updateSplitters();
53664         this.layout();
53665     },
53666
53667     insertRows : function(dm, firstRow, lastRow, isUpdate){
53668         if(firstRow == 0 && lastRow == dm.getCount()-1){
53669             this.refresh();
53670         }else{
53671             if(!isUpdate){
53672                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53673             }
53674             var s = this.getScrollState();
53675             var markup = this.renderRows(firstRow, lastRow);
53676             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53677             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53678             this.restoreScroll(s);
53679             if(!isUpdate){
53680                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53681                 this.syncRowHeights(firstRow, lastRow);
53682                 this.stripeRows(firstRow);
53683                 this.layout();
53684             }
53685         }
53686     },
53687
53688     bufferRows : function(markup, target, index){
53689         var before = null, trows = target.rows, tbody = target.tBodies[0];
53690         if(index < trows.length){
53691             before = trows[index];
53692         }
53693         var b = document.createElement("div");
53694         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53695         var rows = b.firstChild.rows;
53696         for(var i = 0, len = rows.length; i < len; i++){
53697             if(before){
53698                 tbody.insertBefore(rows[0], before);
53699             }else{
53700                 tbody.appendChild(rows[0]);
53701             }
53702         }
53703         b.innerHTML = "";
53704         b = null;
53705     },
53706
53707     deleteRows : function(dm, firstRow, lastRow){
53708         if(dm.getRowCount()<1){
53709             this.fireEvent("beforerefresh", this);
53710             this.mainBody.update("");
53711             this.lockedBody.update("");
53712             this.fireEvent("refresh", this);
53713         }else{
53714             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53715             var bt = this.getBodyTable();
53716             var tbody = bt.firstChild;
53717             var rows = bt.rows;
53718             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53719                 tbody.removeChild(rows[firstRow]);
53720             }
53721             this.stripeRows(firstRow);
53722             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53723         }
53724     },
53725
53726     updateRows : function(dataSource, firstRow, lastRow){
53727         var s = this.getScrollState();
53728         this.refresh();
53729         this.restoreScroll(s);
53730     },
53731
53732     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53733         if(!noRefresh){
53734            this.refresh();
53735         }
53736         this.updateHeaderSortState();
53737     },
53738
53739     getScrollState : function(){
53740         
53741         var sb = this.scroller.dom;
53742         return {left: sb.scrollLeft, top: sb.scrollTop};
53743     },
53744
53745     stripeRows : function(startRow){
53746         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53747             return;
53748         }
53749         startRow = startRow || 0;
53750         var rows = this.getBodyTable().rows;
53751         var lrows = this.getLockedTable().rows;
53752         var cls = ' x-grid-row-alt ';
53753         for(var i = startRow, len = rows.length; i < len; i++){
53754             var row = rows[i], lrow = lrows[i];
53755             var isAlt = ((i+1) % 2 == 0);
53756             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53757             if(isAlt == hasAlt){
53758                 continue;
53759             }
53760             if(isAlt){
53761                 row.className += " x-grid-row-alt";
53762             }else{
53763                 row.className = row.className.replace("x-grid-row-alt", "");
53764             }
53765             if(lrow){
53766                 lrow.className = row.className;
53767             }
53768         }
53769     },
53770
53771     restoreScroll : function(state){
53772         //Roo.log('GridView.restoreScroll');
53773         var sb = this.scroller.dom;
53774         sb.scrollLeft = state.left;
53775         sb.scrollTop = state.top;
53776         this.syncScroll();
53777     },
53778
53779     syncScroll : function(){
53780         //Roo.log('GridView.syncScroll');
53781         var sb = this.scroller.dom;
53782         var sh = this.mainHd.dom;
53783         var bs = this.mainBody.dom;
53784         var lv = this.lockedBody.dom;
53785         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53786         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53787     },
53788
53789     handleScroll : function(e){
53790         this.syncScroll();
53791         var sb = this.scroller.dom;
53792         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53793         e.stopEvent();
53794     },
53795
53796     handleWheel : function(e){
53797         var d = e.getWheelDelta();
53798         this.scroller.dom.scrollTop -= d*22;
53799         // set this here to prevent jumpy scrolling on large tables
53800         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53801         e.stopEvent();
53802     },
53803
53804     renderRows : function(startRow, endRow){
53805         // pull in all the crap needed to render rows
53806         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53807         var colCount = cm.getColumnCount();
53808
53809         if(ds.getCount() < 1){
53810             return ["", ""];
53811         }
53812
53813         // build a map for all the columns
53814         var cs = [];
53815         for(var i = 0; i < colCount; i++){
53816             var name = cm.getDataIndex(i);
53817             cs[i] = {
53818                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53819                 renderer : cm.getRenderer(i),
53820                 id : cm.getColumnId(i),
53821                 locked : cm.isLocked(i)
53822             };
53823         }
53824
53825         startRow = startRow || 0;
53826         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53827
53828         // records to render
53829         var rs = ds.getRange(startRow, endRow);
53830
53831         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53832     },
53833
53834     // As much as I hate to duplicate code, this was branched because FireFox really hates
53835     // [].join("") on strings. The performance difference was substantial enough to
53836     // branch this function
53837     doRender : Roo.isGecko ?
53838             function(cs, rs, ds, startRow, colCount, stripe){
53839                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53840                 // buffers
53841                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53842                 
53843                 var hasListener = this.grid.hasListener('rowclass');
53844                 var rowcfg = {};
53845                 for(var j = 0, len = rs.length; j < len; j++){
53846                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53847                     for(var i = 0; i < colCount; i++){
53848                         c = cs[i];
53849                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53850                         p.id = c.id;
53851                         p.css = p.attr = "";
53852                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53853                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53854                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53855                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53856                         }
53857                         var markup = ct.apply(p);
53858                         if(!c.locked){
53859                             cb+= markup;
53860                         }else{
53861                             lcb+= markup;
53862                         }
53863                     }
53864                     var alt = [];
53865                     if(stripe && ((rowIndex+1) % 2 == 0)){
53866                         alt.push("x-grid-row-alt")
53867                     }
53868                     if(r.dirty){
53869                         alt.push(  " x-grid-dirty-row");
53870                     }
53871                     rp.cells = lcb;
53872                     if(this.getRowClass){
53873                         alt.push(this.getRowClass(r, rowIndex));
53874                     }
53875                     if (hasListener) {
53876                         rowcfg = {
53877                              
53878                             record: r,
53879                             rowIndex : rowIndex,
53880                             rowClass : ''
53881                         }
53882                         this.grid.fireEvent('rowclass', this, rowcfg);
53883                         alt.push(rowcfg.rowClass);
53884                     }
53885                     rp.alt = alt.join(" ");
53886                     lbuf+= rt.apply(rp);
53887                     rp.cells = cb;
53888                     buf+=  rt.apply(rp);
53889                 }
53890                 return [lbuf, buf];
53891             } :
53892             function(cs, rs, ds, startRow, colCount, stripe){
53893                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53894                 // buffers
53895                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53896                 var hasListener = this.grid.hasListener('rowclass');
53897  
53898                 var rowcfg = {};
53899                 for(var j = 0, len = rs.length; j < len; j++){
53900                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53901                     for(var i = 0; i < colCount; i++){
53902                         c = cs[i];
53903                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53904                         p.id = c.id;
53905                         p.css = p.attr = "";
53906                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53907                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53908                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53909                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53910                         }
53911                         
53912                         var markup = ct.apply(p);
53913                         if(!c.locked){
53914                             cb[cb.length] = markup;
53915                         }else{
53916                             lcb[lcb.length] = markup;
53917                         }
53918                     }
53919                     var alt = [];
53920                     if(stripe && ((rowIndex+1) % 2 == 0)){
53921                         alt.push( "x-grid-row-alt");
53922                     }
53923                     if(r.dirty){
53924                         alt.push(" x-grid-dirty-row");
53925                     }
53926                     rp.cells = lcb;
53927                     if(this.getRowClass){
53928                         alt.push( this.getRowClass(r, rowIndex));
53929                     }
53930                     if (hasListener) {
53931                         rowcfg = {
53932                              
53933                             record: r,
53934                             rowIndex : rowIndex,
53935                             rowClass : ''
53936                         }
53937                         this.grid.fireEvent('rowclass', this, rowcfg);
53938                         alt.push(rowcfg.rowClass);
53939                     }
53940                     rp.alt = alt.join(" ");
53941                     rp.cells = lcb.join("");
53942                     lbuf[lbuf.length] = rt.apply(rp);
53943                     rp.cells = cb.join("");
53944                     buf[buf.length] =  rt.apply(rp);
53945                 }
53946                 return [lbuf.join(""), buf.join("")];
53947             },
53948
53949     renderBody : function(){
53950         var markup = this.renderRows();
53951         var bt = this.templates.body;
53952         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53953     },
53954
53955     /**
53956      * Refreshes the grid
53957      * @param {Boolean} headersToo
53958      */
53959     refresh : function(headersToo){
53960         this.fireEvent("beforerefresh", this);
53961         this.grid.stopEditing();
53962         var result = this.renderBody();
53963         this.lockedBody.update(result[0]);
53964         this.mainBody.update(result[1]);
53965         if(headersToo === true){
53966             this.updateHeaders();
53967             this.updateColumns();
53968             this.updateSplitters();
53969             this.updateHeaderSortState();
53970         }
53971         this.syncRowHeights();
53972         this.layout();
53973         this.fireEvent("refresh", this);
53974     },
53975
53976     handleColumnMove : function(cm, oldIndex, newIndex){
53977         this.indexMap = null;
53978         var s = this.getScrollState();
53979         this.refresh(true);
53980         this.restoreScroll(s);
53981         this.afterMove(newIndex);
53982     },
53983
53984     afterMove : function(colIndex){
53985         if(this.enableMoveAnim && Roo.enableFx){
53986             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53987         }
53988         // if multisort - fix sortOrder, and reload..
53989         if (this.grid.dataSource.multiSort) {
53990             // the we can call sort again..
53991             var dm = this.grid.dataSource;
53992             var cm = this.grid.colModel;
53993             var so = [];
53994             for(var i = 0; i < cm.config.length; i++ ) {
53995                 
53996                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53997                     continue; // dont' bother, it's not in sort list or being set.
53998                 }
53999                 
54000                 so.push(cm.config[i].dataIndex);
54001             };
54002             dm.sortOrder = so;
54003             dm.load(dm.lastOptions);
54004             
54005             
54006         }
54007         
54008     },
54009
54010     updateCell : function(dm, rowIndex, dataIndex){
54011         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54012         if(typeof colIndex == "undefined"){ // not present in grid
54013             return;
54014         }
54015         var cm = this.grid.colModel;
54016         var cell = this.getCell(rowIndex, colIndex);
54017         var cellText = this.getCellText(rowIndex, colIndex);
54018
54019         var p = {
54020             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54021             id : cm.getColumnId(colIndex),
54022             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54023         };
54024         var renderer = cm.getRenderer(colIndex);
54025         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54026         if(typeof val == "undefined" || val === "") val = "&#160;";
54027         cellText.innerHTML = val;
54028         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54029         this.syncRowHeights(rowIndex, rowIndex);
54030     },
54031
54032     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54033         var maxWidth = 0;
54034         if(this.grid.autoSizeHeaders){
54035             var h = this.getHeaderCellMeasure(colIndex);
54036             maxWidth = Math.max(maxWidth, h.scrollWidth);
54037         }
54038         var tb, index;
54039         if(this.cm.isLocked(colIndex)){
54040             tb = this.getLockedTable();
54041             index = colIndex;
54042         }else{
54043             tb = this.getBodyTable();
54044             index = colIndex - this.cm.getLockedCount();
54045         }
54046         if(tb && tb.rows){
54047             var rows = tb.rows;
54048             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54049             for(var i = 0; i < stopIndex; i++){
54050                 var cell = rows[i].childNodes[index].firstChild;
54051                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54052             }
54053         }
54054         return maxWidth + /*margin for error in IE*/ 5;
54055     },
54056     /**
54057      * Autofit a column to its content.
54058      * @param {Number} colIndex
54059      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54060      */
54061      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54062          if(this.cm.isHidden(colIndex)){
54063              return; // can't calc a hidden column
54064          }
54065         if(forceMinSize){
54066             var cid = this.cm.getColumnId(colIndex);
54067             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54068            if(this.grid.autoSizeHeaders){
54069                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54070            }
54071         }
54072         var newWidth = this.calcColumnWidth(colIndex);
54073         this.cm.setColumnWidth(colIndex,
54074             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54075         if(!suppressEvent){
54076             this.grid.fireEvent("columnresize", colIndex, newWidth);
54077         }
54078     },
54079
54080     /**
54081      * Autofits all columns to their content and then expands to fit any extra space in the grid
54082      */
54083      autoSizeColumns : function(){
54084         var cm = this.grid.colModel;
54085         var colCount = cm.getColumnCount();
54086         for(var i = 0; i < colCount; i++){
54087             this.autoSizeColumn(i, true, true);
54088         }
54089         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54090             this.fitColumns();
54091         }else{
54092             this.updateColumns();
54093             this.layout();
54094         }
54095     },
54096
54097     /**
54098      * Autofits all columns to the grid's width proportionate with their current size
54099      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54100      */
54101     fitColumns : function(reserveScrollSpace){
54102         var cm = this.grid.colModel;
54103         var colCount = cm.getColumnCount();
54104         var cols = [];
54105         var width = 0;
54106         var i, w;
54107         for (i = 0; i < colCount; i++){
54108             if(!cm.isHidden(i) && !cm.isFixed(i)){
54109                 w = cm.getColumnWidth(i);
54110                 cols.push(i);
54111                 cols.push(w);
54112                 width += w;
54113             }
54114         }
54115         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54116         if(reserveScrollSpace){
54117             avail -= 17;
54118         }
54119         var frac = (avail - cm.getTotalWidth())/width;
54120         while (cols.length){
54121             w = cols.pop();
54122             i = cols.pop();
54123             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54124         }
54125         this.updateColumns();
54126         this.layout();
54127     },
54128
54129     onRowSelect : function(rowIndex){
54130         var row = this.getRowComposite(rowIndex);
54131         row.addClass("x-grid-row-selected");
54132     },
54133
54134     onRowDeselect : function(rowIndex){
54135         var row = this.getRowComposite(rowIndex);
54136         row.removeClass("x-grid-row-selected");
54137     },
54138
54139     onCellSelect : function(row, col){
54140         var cell = this.getCell(row, col);
54141         if(cell){
54142             Roo.fly(cell).addClass("x-grid-cell-selected");
54143         }
54144     },
54145
54146     onCellDeselect : function(row, col){
54147         var cell = this.getCell(row, col);
54148         if(cell){
54149             Roo.fly(cell).removeClass("x-grid-cell-selected");
54150         }
54151     },
54152
54153     updateHeaderSortState : function(){
54154         
54155         // sort state can be single { field: xxx, direction : yyy}
54156         // or   { xxx=>ASC , yyy : DESC ..... }
54157         
54158         var mstate = {};
54159         if (!this.ds.multiSort) { 
54160             var state = this.ds.getSortState();
54161             if(!state){
54162                 return;
54163             }
54164             mstate[state.field] = state.direction;
54165             // FIXME... - this is not used here.. but might be elsewhere..
54166             this.sortState = state;
54167             
54168         } else {
54169             mstate = this.ds.sortToggle;
54170         }
54171         //remove existing sort classes..
54172         
54173         var sc = this.sortClasses;
54174         var hds = this.el.select(this.headerSelector).removeClass(sc);
54175         
54176         for(var f in mstate) {
54177         
54178             var sortColumn = this.cm.findColumnIndex(f);
54179             
54180             if(sortColumn != -1){
54181                 var sortDir = mstate[f];        
54182                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54183             }
54184         }
54185         
54186          
54187         
54188     },
54189
54190
54191     handleHeaderClick : function(g, index,e){
54192         
54193         Roo.log("header click");
54194         
54195         if (Roo.isTouch) {
54196             // touch events on header are handled by context
54197             this.handleHdCtx(g,index,e);
54198             return;
54199         }
54200         
54201         
54202         if(this.headersDisabled){
54203             return;
54204         }
54205         var dm = g.dataSource, cm = g.colModel;
54206         if(!cm.isSortable(index)){
54207             return;
54208         }
54209         g.stopEditing();
54210         
54211         if (dm.multiSort) {
54212             // update the sortOrder
54213             var so = [];
54214             for(var i = 0; i < cm.config.length; i++ ) {
54215                 
54216                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54217                     continue; // dont' bother, it's not in sort list or being set.
54218                 }
54219                 
54220                 so.push(cm.config[i].dataIndex);
54221             };
54222             dm.sortOrder = so;
54223         }
54224         
54225         
54226         dm.sort(cm.getDataIndex(index));
54227     },
54228
54229
54230     destroy : function(){
54231         if(this.colMenu){
54232             this.colMenu.removeAll();
54233             Roo.menu.MenuMgr.unregister(this.colMenu);
54234             this.colMenu.getEl().remove();
54235             delete this.colMenu;
54236         }
54237         if(this.hmenu){
54238             this.hmenu.removeAll();
54239             Roo.menu.MenuMgr.unregister(this.hmenu);
54240             this.hmenu.getEl().remove();
54241             delete this.hmenu;
54242         }
54243         if(this.grid.enableColumnMove){
54244             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54245             if(dds){
54246                 for(var dd in dds){
54247                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54248                         var elid = dds[dd].dragElId;
54249                         dds[dd].unreg();
54250                         Roo.get(elid).remove();
54251                     } else if(dds[dd].config.isTarget){
54252                         dds[dd].proxyTop.remove();
54253                         dds[dd].proxyBottom.remove();
54254                         dds[dd].unreg();
54255                     }
54256                     if(Roo.dd.DDM.locationCache[dd]){
54257                         delete Roo.dd.DDM.locationCache[dd];
54258                     }
54259                 }
54260                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54261             }
54262         }
54263         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54264         this.bind(null, null);
54265         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54266     },
54267
54268     handleLockChange : function(){
54269         this.refresh(true);
54270     },
54271
54272     onDenyColumnLock : function(){
54273
54274     },
54275
54276     onDenyColumnHide : function(){
54277
54278     },
54279
54280     handleHdMenuClick : function(item){
54281         var index = this.hdCtxIndex;
54282         var cm = this.cm, ds = this.ds;
54283         switch(item.id){
54284             case "asc":
54285                 ds.sort(cm.getDataIndex(index), "ASC");
54286                 break;
54287             case "desc":
54288                 ds.sort(cm.getDataIndex(index), "DESC");
54289                 break;
54290             case "lock":
54291                 var lc = cm.getLockedCount();
54292                 if(cm.getColumnCount(true) <= lc+1){
54293                     this.onDenyColumnLock();
54294                     return;
54295                 }
54296                 if(lc != index){
54297                     cm.setLocked(index, true, true);
54298                     cm.moveColumn(index, lc);
54299                     this.grid.fireEvent("columnmove", index, lc);
54300                 }else{
54301                     cm.setLocked(index, true);
54302                 }
54303             break;
54304             case "unlock":
54305                 var lc = cm.getLockedCount();
54306                 if((lc-1) != index){
54307                     cm.setLocked(index, false, true);
54308                     cm.moveColumn(index, lc-1);
54309                     this.grid.fireEvent("columnmove", index, lc-1);
54310                 }else{
54311                     cm.setLocked(index, false);
54312                 }
54313             break;
54314             case 'wider': // used to expand cols on touch..
54315             case 'narrow':
54316                 var cw = cm.getColumnWidth(index);
54317                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54318                 cw = Math.max(0, cw);
54319                 cw = Math.min(cw,4000);
54320                 cm.setColumnWidth(index, cw);
54321                 break;
54322                 
54323             default:
54324                 index = cm.getIndexById(item.id.substr(4));
54325                 if(index != -1){
54326                     if(item.checked && cm.getColumnCount(true) <= 1){
54327                         this.onDenyColumnHide();
54328                         return false;
54329                     }
54330                     cm.setHidden(index, item.checked);
54331                 }
54332         }
54333         return true;
54334     },
54335
54336     beforeColMenuShow : function(){
54337         var cm = this.cm,  colCount = cm.getColumnCount();
54338         this.colMenu.removeAll();
54339         for(var i = 0; i < colCount; i++){
54340             this.colMenu.add(new Roo.menu.CheckItem({
54341                 id: "col-"+cm.getColumnId(i),
54342                 text: cm.getColumnHeader(i),
54343                 checked: !cm.isHidden(i),
54344                 hideOnClick:false
54345             }));
54346         }
54347     },
54348
54349     handleHdCtx : function(g, index, e){
54350         e.stopEvent();
54351         var hd = this.getHeaderCell(index);
54352         this.hdCtxIndex = index;
54353         var ms = this.hmenu.items, cm = this.cm;
54354         ms.get("asc").setDisabled(!cm.isSortable(index));
54355         ms.get("desc").setDisabled(!cm.isSortable(index));
54356         if(this.grid.enableColLock !== false){
54357             ms.get("lock").setDisabled(cm.isLocked(index));
54358             ms.get("unlock").setDisabled(!cm.isLocked(index));
54359         }
54360         this.hmenu.show(hd, "tl-bl");
54361     },
54362
54363     handleHdOver : function(e){
54364         var hd = this.findHeaderCell(e.getTarget());
54365         if(hd && !this.headersDisabled){
54366             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54367                this.fly(hd).addClass("x-grid-hd-over");
54368             }
54369         }
54370     },
54371
54372     handleHdOut : function(e){
54373         var hd = this.findHeaderCell(e.getTarget());
54374         if(hd){
54375             this.fly(hd).removeClass("x-grid-hd-over");
54376         }
54377     },
54378
54379     handleSplitDblClick : function(e, t){
54380         var i = this.getCellIndex(t);
54381         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54382             this.autoSizeColumn(i, true);
54383             this.layout();
54384         }
54385     },
54386
54387     render : function(){
54388
54389         var cm = this.cm;
54390         var colCount = cm.getColumnCount();
54391
54392         if(this.grid.monitorWindowResize === true){
54393             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54394         }
54395         var header = this.renderHeaders();
54396         var body = this.templates.body.apply({rows:""});
54397         var html = this.templates.master.apply({
54398             lockedBody: body,
54399             body: body,
54400             lockedHeader: header[0],
54401             header: header[1]
54402         });
54403
54404         //this.updateColumns();
54405
54406         this.grid.getGridEl().dom.innerHTML = html;
54407
54408         this.initElements();
54409         
54410         // a kludge to fix the random scolling effect in webkit
54411         this.el.on("scroll", function() {
54412             this.el.dom.scrollTop=0; // hopefully not recursive..
54413         },this);
54414
54415         this.scroller.on("scroll", this.handleScroll, this);
54416         this.lockedBody.on("mousewheel", this.handleWheel, this);
54417         this.mainBody.on("mousewheel", this.handleWheel, this);
54418
54419         this.mainHd.on("mouseover", this.handleHdOver, this);
54420         this.mainHd.on("mouseout", this.handleHdOut, this);
54421         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54422                 {delegate: "."+this.splitClass});
54423
54424         this.lockedHd.on("mouseover", this.handleHdOver, this);
54425         this.lockedHd.on("mouseout", this.handleHdOut, this);
54426         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54427                 {delegate: "."+this.splitClass});
54428
54429         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54430             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54431         }
54432
54433         this.updateSplitters();
54434
54435         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54436             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54437             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54438         }
54439
54440         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54441             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54442             this.hmenu.add(
54443                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54444                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54445             );
54446             if(this.grid.enableColLock !== false){
54447                 this.hmenu.add('-',
54448                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54449                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54450                 );
54451             }
54452             if (Roo.isTouch) {
54453                  this.hmenu.add('-',
54454                     {id:"wider", text: this.columnsWiderText},
54455                     {id:"narrow", text: this.columnsNarrowText }
54456                 );
54457                 
54458                  
54459             }
54460             
54461             if(this.grid.enableColumnHide !== false){
54462
54463                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54464                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54465                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54466
54467                 this.hmenu.add('-',
54468                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54469                 );
54470             }
54471             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54472
54473             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54474         }
54475
54476         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54477             this.dd = new Roo.grid.GridDragZone(this.grid, {
54478                 ddGroup : this.grid.ddGroup || 'GridDD'
54479             });
54480             
54481         }
54482
54483         /*
54484         for(var i = 0; i < colCount; i++){
54485             if(cm.isHidden(i)){
54486                 this.hideColumn(i);
54487             }
54488             if(cm.config[i].align){
54489                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54490                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54491             }
54492         }*/
54493         
54494         this.updateHeaderSortState();
54495
54496         this.beforeInitialResize();
54497         this.layout(true);
54498
54499         // two part rendering gives faster view to the user
54500         this.renderPhase2.defer(1, this);
54501     },
54502
54503     renderPhase2 : function(){
54504         // render the rows now
54505         this.refresh();
54506         if(this.grid.autoSizeColumns){
54507             this.autoSizeColumns();
54508         }
54509     },
54510
54511     beforeInitialResize : function(){
54512
54513     },
54514
54515     onColumnSplitterMoved : function(i, w){
54516         this.userResized = true;
54517         var cm = this.grid.colModel;
54518         cm.setColumnWidth(i, w, true);
54519         var cid = cm.getColumnId(i);
54520         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54521         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54522         this.updateSplitters();
54523         this.layout();
54524         this.grid.fireEvent("columnresize", i, w);
54525     },
54526
54527     syncRowHeights : function(startIndex, endIndex){
54528         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54529             startIndex = startIndex || 0;
54530             var mrows = this.getBodyTable().rows;
54531             var lrows = this.getLockedTable().rows;
54532             var len = mrows.length-1;
54533             endIndex = Math.min(endIndex || len, len);
54534             for(var i = startIndex; i <= endIndex; i++){
54535                 var m = mrows[i], l = lrows[i];
54536                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54537                 m.style.height = l.style.height = h + "px";
54538             }
54539         }
54540     },
54541
54542     layout : function(initialRender, is2ndPass){
54543         var g = this.grid;
54544         var auto = g.autoHeight;
54545         var scrollOffset = 16;
54546         var c = g.getGridEl(), cm = this.cm,
54547                 expandCol = g.autoExpandColumn,
54548                 gv = this;
54549         //c.beginMeasure();
54550
54551         if(!c.dom.offsetWidth){ // display:none?
54552             if(initialRender){
54553                 this.lockedWrap.show();
54554                 this.mainWrap.show();
54555             }
54556             return;
54557         }
54558
54559         var hasLock = this.cm.isLocked(0);
54560
54561         var tbh = this.headerPanel.getHeight();
54562         var bbh = this.footerPanel.getHeight();
54563
54564         if(auto){
54565             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54566             var newHeight = ch + c.getBorderWidth("tb");
54567             if(g.maxHeight){
54568                 newHeight = Math.min(g.maxHeight, newHeight);
54569             }
54570             c.setHeight(newHeight);
54571         }
54572
54573         if(g.autoWidth){
54574             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54575         }
54576
54577         var s = this.scroller;
54578
54579         var csize = c.getSize(true);
54580
54581         this.el.setSize(csize.width, csize.height);
54582
54583         this.headerPanel.setWidth(csize.width);
54584         this.footerPanel.setWidth(csize.width);
54585
54586         var hdHeight = this.mainHd.getHeight();
54587         var vw = csize.width;
54588         var vh = csize.height - (tbh + bbh);
54589
54590         s.setSize(vw, vh);
54591
54592         var bt = this.getBodyTable();
54593         var ltWidth = hasLock ?
54594                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54595
54596         var scrollHeight = bt.offsetHeight;
54597         var scrollWidth = ltWidth + bt.offsetWidth;
54598         var vscroll = false, hscroll = false;
54599
54600         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54601
54602         var lw = this.lockedWrap, mw = this.mainWrap;
54603         var lb = this.lockedBody, mb = this.mainBody;
54604
54605         setTimeout(function(){
54606             var t = s.dom.offsetTop;
54607             var w = s.dom.clientWidth,
54608                 h = s.dom.clientHeight;
54609
54610             lw.setTop(t);
54611             lw.setSize(ltWidth, h);
54612
54613             mw.setLeftTop(ltWidth, t);
54614             mw.setSize(w-ltWidth, h);
54615
54616             lb.setHeight(h-hdHeight);
54617             mb.setHeight(h-hdHeight);
54618
54619             if(is2ndPass !== true && !gv.userResized && expandCol){
54620                 // high speed resize without full column calculation
54621                 
54622                 var ci = cm.getIndexById(expandCol);
54623                 if (ci < 0) {
54624                     ci = cm.findColumnIndex(expandCol);
54625                 }
54626                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54627                 var expandId = cm.getColumnId(ci);
54628                 var  tw = cm.getTotalWidth(false);
54629                 var currentWidth = cm.getColumnWidth(ci);
54630                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54631                 if(currentWidth != cw){
54632                     cm.setColumnWidth(ci, cw, true);
54633                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54634                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54635                     gv.updateSplitters();
54636                     gv.layout(false, true);
54637                 }
54638             }
54639
54640             if(initialRender){
54641                 lw.show();
54642                 mw.show();
54643             }
54644             //c.endMeasure();
54645         }, 10);
54646     },
54647
54648     onWindowResize : function(){
54649         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54650             return;
54651         }
54652         this.layout();
54653     },
54654
54655     appendFooter : function(parentEl){
54656         return null;
54657     },
54658
54659     sortAscText : "Sort Ascending",
54660     sortDescText : "Sort Descending",
54661     lockText : "Lock Column",
54662     unlockText : "Unlock Column",
54663     columnsText : "Columns",
54664  
54665     columnsWiderText : "Wider",
54666     columnsNarrowText : "Thinner"
54667 });
54668
54669
54670 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54671     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54672     this.proxy.el.addClass('x-grid3-col-dd');
54673 };
54674
54675 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54676     handleMouseDown : function(e){
54677
54678     },
54679
54680     callHandleMouseDown : function(e){
54681         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54682     }
54683 });
54684 /*
54685  * Based on:
54686  * Ext JS Library 1.1.1
54687  * Copyright(c) 2006-2007, Ext JS, LLC.
54688  *
54689  * Originally Released Under LGPL - original licence link has changed is not relivant.
54690  *
54691  * Fork - LGPL
54692  * <script type="text/javascript">
54693  */
54694  
54695 // private
54696 // This is a support class used internally by the Grid components
54697 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54698     this.grid = grid;
54699     this.view = grid.getView();
54700     this.proxy = this.view.resizeProxy;
54701     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54702         "gridSplitters" + this.grid.getGridEl().id, {
54703         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54704     });
54705     this.setHandleElId(Roo.id(hd));
54706     this.setOuterHandleElId(Roo.id(hd2));
54707     this.scroll = false;
54708 };
54709 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54710     fly: Roo.Element.fly,
54711
54712     b4StartDrag : function(x, y){
54713         this.view.headersDisabled = true;
54714         this.proxy.setHeight(this.view.mainWrap.getHeight());
54715         var w = this.cm.getColumnWidth(this.cellIndex);
54716         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54717         this.resetConstraints();
54718         this.setXConstraint(minw, 1000);
54719         this.setYConstraint(0, 0);
54720         this.minX = x - minw;
54721         this.maxX = x + 1000;
54722         this.startPos = x;
54723         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54724     },
54725
54726
54727     handleMouseDown : function(e){
54728         ev = Roo.EventObject.setEvent(e);
54729         var t = this.fly(ev.getTarget());
54730         if(t.hasClass("x-grid-split")){
54731             this.cellIndex = this.view.getCellIndex(t.dom);
54732             this.split = t.dom;
54733             this.cm = this.grid.colModel;
54734             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54735                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54736             }
54737         }
54738     },
54739
54740     endDrag : function(e){
54741         this.view.headersDisabled = false;
54742         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54743         var diff = endX - this.startPos;
54744         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54745     },
54746
54747     autoOffset : function(){
54748         this.setDelta(0,0);
54749     }
54750 });/*
54751  * Based on:
54752  * Ext JS Library 1.1.1
54753  * Copyright(c) 2006-2007, Ext JS, LLC.
54754  *
54755  * Originally Released Under LGPL - original licence link has changed is not relivant.
54756  *
54757  * Fork - LGPL
54758  * <script type="text/javascript">
54759  */
54760  
54761 // private
54762 // This is a support class used internally by the Grid components
54763 Roo.grid.GridDragZone = function(grid, config){
54764     this.view = grid.getView();
54765     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54766     if(this.view.lockedBody){
54767         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54768         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54769     }
54770     this.scroll = false;
54771     this.grid = grid;
54772     this.ddel = document.createElement('div');
54773     this.ddel.className = 'x-grid-dd-wrap';
54774 };
54775
54776 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54777     ddGroup : "GridDD",
54778
54779     getDragData : function(e){
54780         var t = Roo.lib.Event.getTarget(e);
54781         var rowIndex = this.view.findRowIndex(t);
54782         var sm = this.grid.selModel;
54783             
54784         //Roo.log(rowIndex);
54785         
54786         if (sm.getSelectedCell) {
54787             // cell selection..
54788             if (!sm.getSelectedCell()) {
54789                 return false;
54790             }
54791             if (rowIndex != sm.getSelectedCell()[0]) {
54792                 return false;
54793             }
54794         
54795         }
54796         
54797         if(rowIndex !== false){
54798             
54799             // if editorgrid.. 
54800             
54801             
54802             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54803                
54804             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54805               //  
54806             //}
54807             if (e.hasModifier()){
54808                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54809             }
54810             
54811             Roo.log("getDragData");
54812             
54813             return {
54814                 grid: this.grid,
54815                 ddel: this.ddel,
54816                 rowIndex: rowIndex,
54817                 selections:sm.getSelections ? sm.getSelections() : (
54818                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54819                 )
54820             };
54821         }
54822         return false;
54823     },
54824
54825     onInitDrag : function(e){
54826         var data = this.dragData;
54827         this.ddel.innerHTML = this.grid.getDragDropText();
54828         this.proxy.update(this.ddel);
54829         // fire start drag?
54830     },
54831
54832     afterRepair : function(){
54833         this.dragging = false;
54834     },
54835
54836     getRepairXY : function(e, data){
54837         return false;
54838     },
54839
54840     onEndDrag : function(data, e){
54841         // fire end drag?
54842     },
54843
54844     onValidDrop : function(dd, e, id){
54845         // fire drag drop?
54846         this.hideProxy();
54847     },
54848
54849     beforeInvalidDrop : function(e, id){
54850
54851     }
54852 });/*
54853  * Based on:
54854  * Ext JS Library 1.1.1
54855  * Copyright(c) 2006-2007, Ext JS, LLC.
54856  *
54857  * Originally Released Under LGPL - original licence link has changed is not relivant.
54858  *
54859  * Fork - LGPL
54860  * <script type="text/javascript">
54861  */
54862  
54863
54864 /**
54865  * @class Roo.grid.ColumnModel
54866  * @extends Roo.util.Observable
54867  * This is the default implementation of a ColumnModel used by the Grid. It defines
54868  * the columns in the grid.
54869  * <br>Usage:<br>
54870  <pre><code>
54871  var colModel = new Roo.grid.ColumnModel([
54872         {header: "Ticker", width: 60, sortable: true, locked: true},
54873         {header: "Company Name", width: 150, sortable: true},
54874         {header: "Market Cap.", width: 100, sortable: true},
54875         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54876         {header: "Employees", width: 100, sortable: true, resizable: false}
54877  ]);
54878  </code></pre>
54879  * <p>
54880  
54881  * The config options listed for this class are options which may appear in each
54882  * individual column definition.
54883  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54884  * @constructor
54885  * @param {Object} config An Array of column config objects. See this class's
54886  * config objects for details.
54887 */
54888 Roo.grid.ColumnModel = function(config){
54889         /**
54890      * The config passed into the constructor
54891      */
54892     this.config = config;
54893     this.lookup = {};
54894
54895     // if no id, create one
54896     // if the column does not have a dataIndex mapping,
54897     // map it to the order it is in the config
54898     for(var i = 0, len = config.length; i < len; i++){
54899         var c = config[i];
54900         if(typeof c.dataIndex == "undefined"){
54901             c.dataIndex = i;
54902         }
54903         if(typeof c.renderer == "string"){
54904             c.renderer = Roo.util.Format[c.renderer];
54905         }
54906         if(typeof c.id == "undefined"){
54907             c.id = Roo.id();
54908         }
54909         if(c.editor && c.editor.xtype){
54910             c.editor  = Roo.factory(c.editor, Roo.grid);
54911         }
54912         if(c.editor && c.editor.isFormField){
54913             c.editor = new Roo.grid.GridEditor(c.editor);
54914         }
54915         this.lookup[c.id] = c;
54916     }
54917
54918     /**
54919      * The width of columns which have no width specified (defaults to 100)
54920      * @type Number
54921      */
54922     this.defaultWidth = 100;
54923
54924     /**
54925      * Default sortable of columns which have no sortable specified (defaults to false)
54926      * @type Boolean
54927      */
54928     this.defaultSortable = false;
54929
54930     this.addEvents({
54931         /**
54932              * @event widthchange
54933              * Fires when the width of a column changes.
54934              * @param {ColumnModel} this
54935              * @param {Number} columnIndex The column index
54936              * @param {Number} newWidth The new width
54937              */
54938             "widthchange": true,
54939         /**
54940              * @event headerchange
54941              * Fires when the text of a header changes.
54942              * @param {ColumnModel} this
54943              * @param {Number} columnIndex The column index
54944              * @param {Number} newText The new header text
54945              */
54946             "headerchange": true,
54947         /**
54948              * @event hiddenchange
54949              * Fires when a column is hidden or "unhidden".
54950              * @param {ColumnModel} this
54951              * @param {Number} columnIndex The column index
54952              * @param {Boolean} hidden true if hidden, false otherwise
54953              */
54954             "hiddenchange": true,
54955             /**
54956          * @event columnmoved
54957          * Fires when a column is moved.
54958          * @param {ColumnModel} this
54959          * @param {Number} oldIndex
54960          * @param {Number} newIndex
54961          */
54962         "columnmoved" : true,
54963         /**
54964          * @event columlockchange
54965          * Fires when a column's locked state is changed
54966          * @param {ColumnModel} this
54967          * @param {Number} colIndex
54968          * @param {Boolean} locked true if locked
54969          */
54970         "columnlockchange" : true
54971     });
54972     Roo.grid.ColumnModel.superclass.constructor.call(this);
54973 };
54974 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54975     /**
54976      * @cfg {String} header The header text to display in the Grid view.
54977      */
54978     /**
54979      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54980      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54981      * specified, the column's index is used as an index into the Record's data Array.
54982      */
54983     /**
54984      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54985      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54986      */
54987     /**
54988      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54989      * Defaults to the value of the {@link #defaultSortable} property.
54990      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54991      */
54992     /**
54993      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54994      */
54995     /**
54996      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54997      */
54998     /**
54999      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55000      */
55001     /**
55002      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55003      */
55004     /**
55005      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55006      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55007      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55008      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55009      */
55010        /**
55011      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55012      */
55013     /**
55014      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55015      */
55016
55017     /**
55018      * Returns the id of the column at the specified index.
55019      * @param {Number} index The column index
55020      * @return {String} the id
55021      */
55022     getColumnId : function(index){
55023         return this.config[index].id;
55024     },
55025
55026     /**
55027      * Returns the column for a specified id.
55028      * @param {String} id The column id
55029      * @return {Object} the column
55030      */
55031     getColumnById : function(id){
55032         return this.lookup[id];
55033     },
55034
55035     
55036     /**
55037      * Returns the column for a specified dataIndex.
55038      * @param {String} dataIndex The column dataIndex
55039      * @return {Object|Boolean} the column or false if not found
55040      */
55041     getColumnByDataIndex: function(dataIndex){
55042         var index = this.findColumnIndex(dataIndex);
55043         return index > -1 ? this.config[index] : false;
55044     },
55045     
55046     /**
55047      * Returns the index for a specified column id.
55048      * @param {String} id The column id
55049      * @return {Number} the index, or -1 if not found
55050      */
55051     getIndexById : function(id){
55052         for(var i = 0, len = this.config.length; i < len; i++){
55053             if(this.config[i].id == id){
55054                 return i;
55055             }
55056         }
55057         return -1;
55058     },
55059     
55060     /**
55061      * Returns the index for a specified column dataIndex.
55062      * @param {String} dataIndex The column dataIndex
55063      * @return {Number} the index, or -1 if not found
55064      */
55065     
55066     findColumnIndex : function(dataIndex){
55067         for(var i = 0, len = this.config.length; i < len; i++){
55068             if(this.config[i].dataIndex == dataIndex){
55069                 return i;
55070             }
55071         }
55072         return -1;
55073     },
55074     
55075     
55076     moveColumn : function(oldIndex, newIndex){
55077         var c = this.config[oldIndex];
55078         this.config.splice(oldIndex, 1);
55079         this.config.splice(newIndex, 0, c);
55080         this.dataMap = null;
55081         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55082     },
55083
55084     isLocked : function(colIndex){
55085         return this.config[colIndex].locked === true;
55086     },
55087
55088     setLocked : function(colIndex, value, suppressEvent){
55089         if(this.isLocked(colIndex) == value){
55090             return;
55091         }
55092         this.config[colIndex].locked = value;
55093         if(!suppressEvent){
55094             this.fireEvent("columnlockchange", this, colIndex, value);
55095         }
55096     },
55097
55098     getTotalLockedWidth : function(){
55099         var totalWidth = 0;
55100         for(var i = 0; i < this.config.length; i++){
55101             if(this.isLocked(i) && !this.isHidden(i)){
55102                 this.totalWidth += this.getColumnWidth(i);
55103             }
55104         }
55105         return totalWidth;
55106     },
55107
55108     getLockedCount : function(){
55109         for(var i = 0, len = this.config.length; i < len; i++){
55110             if(!this.isLocked(i)){
55111                 return i;
55112             }
55113         }
55114     },
55115
55116     /**
55117      * Returns the number of columns.
55118      * @return {Number}
55119      */
55120     getColumnCount : function(visibleOnly){
55121         if(visibleOnly === true){
55122             var c = 0;
55123             for(var i = 0, len = this.config.length; i < len; i++){
55124                 if(!this.isHidden(i)){
55125                     c++;
55126                 }
55127             }
55128             return c;
55129         }
55130         return this.config.length;
55131     },
55132
55133     /**
55134      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55135      * @param {Function} fn
55136      * @param {Object} scope (optional)
55137      * @return {Array} result
55138      */
55139     getColumnsBy : function(fn, scope){
55140         var r = [];
55141         for(var i = 0, len = this.config.length; i < len; i++){
55142             var c = this.config[i];
55143             if(fn.call(scope||this, c, i) === true){
55144                 r[r.length] = c;
55145             }
55146         }
55147         return r;
55148     },
55149
55150     /**
55151      * Returns true if the specified column is sortable.
55152      * @param {Number} col The column index
55153      * @return {Boolean}
55154      */
55155     isSortable : function(col){
55156         if(typeof this.config[col].sortable == "undefined"){
55157             return this.defaultSortable;
55158         }
55159         return this.config[col].sortable;
55160     },
55161
55162     /**
55163      * Returns the rendering (formatting) function defined for the column.
55164      * @param {Number} col The column index.
55165      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55166      */
55167     getRenderer : function(col){
55168         if(!this.config[col].renderer){
55169             return Roo.grid.ColumnModel.defaultRenderer;
55170         }
55171         return this.config[col].renderer;
55172     },
55173
55174     /**
55175      * Sets the rendering (formatting) function for a column.
55176      * @param {Number} col The column index
55177      * @param {Function} fn The function to use to process the cell's raw data
55178      * to return HTML markup for the grid view. The render function is called with
55179      * the following parameters:<ul>
55180      * <li>Data value.</li>
55181      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55182      * <li>css A CSS style string to apply to the table cell.</li>
55183      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55184      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55185      * <li>Row index</li>
55186      * <li>Column index</li>
55187      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55188      */
55189     setRenderer : function(col, fn){
55190         this.config[col].renderer = fn;
55191     },
55192
55193     /**
55194      * Returns the width for the specified column.
55195      * @param {Number} col The column index
55196      * @return {Number}
55197      */
55198     getColumnWidth : function(col){
55199         return this.config[col].width * 1 || this.defaultWidth;
55200     },
55201
55202     /**
55203      * Sets the width for a column.
55204      * @param {Number} col The column index
55205      * @param {Number} width The new width
55206      */
55207     setColumnWidth : function(col, width, suppressEvent){
55208         this.config[col].width = width;
55209         this.totalWidth = null;
55210         if(!suppressEvent){
55211              this.fireEvent("widthchange", this, col, width);
55212         }
55213     },
55214
55215     /**
55216      * Returns the total width of all columns.
55217      * @param {Boolean} includeHidden True to include hidden column widths
55218      * @return {Number}
55219      */
55220     getTotalWidth : function(includeHidden){
55221         if(!this.totalWidth){
55222             this.totalWidth = 0;
55223             for(var i = 0, len = this.config.length; i < len; i++){
55224                 if(includeHidden || !this.isHidden(i)){
55225                     this.totalWidth += this.getColumnWidth(i);
55226                 }
55227             }
55228         }
55229         return this.totalWidth;
55230     },
55231
55232     /**
55233      * Returns the header for the specified column.
55234      * @param {Number} col The column index
55235      * @return {String}
55236      */
55237     getColumnHeader : function(col){
55238         return this.config[col].header;
55239     },
55240
55241     /**
55242      * Sets the header for a column.
55243      * @param {Number} col The column index
55244      * @param {String} header The new header
55245      */
55246     setColumnHeader : function(col, header){
55247         this.config[col].header = header;
55248         this.fireEvent("headerchange", this, col, header);
55249     },
55250
55251     /**
55252      * Returns the tooltip for the specified column.
55253      * @param {Number} col The column index
55254      * @return {String}
55255      */
55256     getColumnTooltip : function(col){
55257             return this.config[col].tooltip;
55258     },
55259     /**
55260      * Sets the tooltip for a column.
55261      * @param {Number} col The column index
55262      * @param {String} tooltip The new tooltip
55263      */
55264     setColumnTooltip : function(col, tooltip){
55265             this.config[col].tooltip = tooltip;
55266     },
55267
55268     /**
55269      * Returns the dataIndex for the specified column.
55270      * @param {Number} col The column index
55271      * @return {Number}
55272      */
55273     getDataIndex : function(col){
55274         return this.config[col].dataIndex;
55275     },
55276
55277     /**
55278      * Sets the dataIndex for a column.
55279      * @param {Number} col The column index
55280      * @param {Number} dataIndex The new dataIndex
55281      */
55282     setDataIndex : function(col, dataIndex){
55283         this.config[col].dataIndex = dataIndex;
55284     },
55285
55286     
55287     
55288     /**
55289      * Returns true if the cell is editable.
55290      * @param {Number} colIndex The column index
55291      * @param {Number} rowIndex The row index
55292      * @return {Boolean}
55293      */
55294     isCellEditable : function(colIndex, rowIndex){
55295         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55296     },
55297
55298     /**
55299      * Returns the editor defined for the cell/column.
55300      * return false or null to disable editing.
55301      * @param {Number} colIndex The column index
55302      * @param {Number} rowIndex The row index
55303      * @return {Object}
55304      */
55305     getCellEditor : function(colIndex, rowIndex){
55306         return this.config[colIndex].editor;
55307     },
55308
55309     /**
55310      * Sets if a column is editable.
55311      * @param {Number} col The column index
55312      * @param {Boolean} editable True if the column is editable
55313      */
55314     setEditable : function(col, editable){
55315         this.config[col].editable = editable;
55316     },
55317
55318
55319     /**
55320      * Returns true if the column is hidden.
55321      * @param {Number} colIndex The column index
55322      * @return {Boolean}
55323      */
55324     isHidden : function(colIndex){
55325         return this.config[colIndex].hidden;
55326     },
55327
55328
55329     /**
55330      * Returns true if the column width cannot be changed
55331      */
55332     isFixed : function(colIndex){
55333         return this.config[colIndex].fixed;
55334     },
55335
55336     /**
55337      * Returns true if the column can be resized
55338      * @return {Boolean}
55339      */
55340     isResizable : function(colIndex){
55341         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55342     },
55343     /**
55344      * Sets if a column is hidden.
55345      * @param {Number} colIndex The column index
55346      * @param {Boolean} hidden True if the column is hidden
55347      */
55348     setHidden : function(colIndex, hidden){
55349         this.config[colIndex].hidden = hidden;
55350         this.totalWidth = null;
55351         this.fireEvent("hiddenchange", this, colIndex, hidden);
55352     },
55353
55354     /**
55355      * Sets the editor for a column.
55356      * @param {Number} col The column index
55357      * @param {Object} editor The editor object
55358      */
55359     setEditor : function(col, editor){
55360         this.config[col].editor = editor;
55361     }
55362 });
55363
55364 Roo.grid.ColumnModel.defaultRenderer = function(value){
55365         if(typeof value == "string" && value.length < 1){
55366             return "&#160;";
55367         }
55368         return value;
55369 };
55370
55371 // Alias for backwards compatibility
55372 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55373 /*
55374  * Based on:
55375  * Ext JS Library 1.1.1
55376  * Copyright(c) 2006-2007, Ext JS, LLC.
55377  *
55378  * Originally Released Under LGPL - original licence link has changed is not relivant.
55379  *
55380  * Fork - LGPL
55381  * <script type="text/javascript">
55382  */
55383
55384 /**
55385  * @class Roo.grid.AbstractSelectionModel
55386  * @extends Roo.util.Observable
55387  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55388  * implemented by descendant classes.  This class should not be directly instantiated.
55389  * @constructor
55390  */
55391 Roo.grid.AbstractSelectionModel = function(){
55392     this.locked = false;
55393     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55394 };
55395
55396 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55397     /** @ignore Called by the grid automatically. Do not call directly. */
55398     init : function(grid){
55399         this.grid = grid;
55400         this.initEvents();
55401     },
55402
55403     /**
55404      * Locks the selections.
55405      */
55406     lock : function(){
55407         this.locked = true;
55408     },
55409
55410     /**
55411      * Unlocks the selections.
55412      */
55413     unlock : function(){
55414         this.locked = false;
55415     },
55416
55417     /**
55418      * Returns true if the selections are locked.
55419      * @return {Boolean}
55420      */
55421     isLocked : function(){
55422         return this.locked;
55423     }
55424 });/*
55425  * Based on:
55426  * Ext JS Library 1.1.1
55427  * Copyright(c) 2006-2007, Ext JS, LLC.
55428  *
55429  * Originally Released Under LGPL - original licence link has changed is not relivant.
55430  *
55431  * Fork - LGPL
55432  * <script type="text/javascript">
55433  */
55434 /**
55435  * @extends Roo.grid.AbstractSelectionModel
55436  * @class Roo.grid.RowSelectionModel
55437  * The default SelectionModel used by {@link Roo.grid.Grid}.
55438  * It supports multiple selections and keyboard selection/navigation. 
55439  * @constructor
55440  * @param {Object} config
55441  */
55442 Roo.grid.RowSelectionModel = function(config){
55443     Roo.apply(this, config);
55444     this.selections = new Roo.util.MixedCollection(false, function(o){
55445         return o.id;
55446     });
55447
55448     this.last = false;
55449     this.lastActive = false;
55450
55451     this.addEvents({
55452         /**
55453              * @event selectionchange
55454              * Fires when the selection changes
55455              * @param {SelectionModel} this
55456              */
55457             "selectionchange" : true,
55458         /**
55459              * @event afterselectionchange
55460              * Fires after the selection changes (eg. by key press or clicking)
55461              * @param {SelectionModel} this
55462              */
55463             "afterselectionchange" : true,
55464         /**
55465              * @event beforerowselect
55466              * Fires when a row is selected being selected, return false to cancel.
55467              * @param {SelectionModel} this
55468              * @param {Number} rowIndex The selected index
55469              * @param {Boolean} keepExisting False if other selections will be cleared
55470              */
55471             "beforerowselect" : true,
55472         /**
55473              * @event rowselect
55474              * Fires when a row is selected.
55475              * @param {SelectionModel} this
55476              * @param {Number} rowIndex The selected index
55477              * @param {Roo.data.Record} r The record
55478              */
55479             "rowselect" : true,
55480         /**
55481              * @event rowdeselect
55482              * Fires when a row is deselected.
55483              * @param {SelectionModel} this
55484              * @param {Number} rowIndex The selected index
55485              */
55486         "rowdeselect" : true
55487     });
55488     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55489     this.locked = false;
55490 };
55491
55492 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55493     /**
55494      * @cfg {Boolean} singleSelect
55495      * True to allow selection of only one row at a time (defaults to false)
55496      */
55497     singleSelect : false,
55498
55499     // private
55500     initEvents : function(){
55501
55502         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55503             this.grid.on("mousedown", this.handleMouseDown, this);
55504         }else{ // allow click to work like normal
55505             this.grid.on("rowclick", this.handleDragableRowClick, this);
55506         }
55507
55508         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55509             "up" : function(e){
55510                 if(!e.shiftKey){
55511                     this.selectPrevious(e.shiftKey);
55512                 }else if(this.last !== false && this.lastActive !== false){
55513                     var last = this.last;
55514                     this.selectRange(this.last,  this.lastActive-1);
55515                     this.grid.getView().focusRow(this.lastActive);
55516                     if(last !== false){
55517                         this.last = last;
55518                     }
55519                 }else{
55520                     this.selectFirstRow();
55521                 }
55522                 this.fireEvent("afterselectionchange", this);
55523             },
55524             "down" : function(e){
55525                 if(!e.shiftKey){
55526                     this.selectNext(e.shiftKey);
55527                 }else if(this.last !== false && this.lastActive !== false){
55528                     var last = this.last;
55529                     this.selectRange(this.last,  this.lastActive+1);
55530                     this.grid.getView().focusRow(this.lastActive);
55531                     if(last !== false){
55532                         this.last = last;
55533                     }
55534                 }else{
55535                     this.selectFirstRow();
55536                 }
55537                 this.fireEvent("afterselectionchange", this);
55538             },
55539             scope: this
55540         });
55541
55542         var view = this.grid.view;
55543         view.on("refresh", this.onRefresh, this);
55544         view.on("rowupdated", this.onRowUpdated, this);
55545         view.on("rowremoved", this.onRemove, this);
55546     },
55547
55548     // private
55549     onRefresh : function(){
55550         var ds = this.grid.dataSource, i, v = this.grid.view;
55551         var s = this.selections;
55552         s.each(function(r){
55553             if((i = ds.indexOfId(r.id)) != -1){
55554                 v.onRowSelect(i);
55555             }else{
55556                 s.remove(r);
55557             }
55558         });
55559     },
55560
55561     // private
55562     onRemove : function(v, index, r){
55563         this.selections.remove(r);
55564     },
55565
55566     // private
55567     onRowUpdated : function(v, index, r){
55568         if(this.isSelected(r)){
55569             v.onRowSelect(index);
55570         }
55571     },
55572
55573     /**
55574      * Select records.
55575      * @param {Array} records The records to select
55576      * @param {Boolean} keepExisting (optional) True to keep existing selections
55577      */
55578     selectRecords : function(records, keepExisting){
55579         if(!keepExisting){
55580             this.clearSelections();
55581         }
55582         var ds = this.grid.dataSource;
55583         for(var i = 0, len = records.length; i < len; i++){
55584             this.selectRow(ds.indexOf(records[i]), true);
55585         }
55586     },
55587
55588     /**
55589      * Gets the number of selected rows.
55590      * @return {Number}
55591      */
55592     getCount : function(){
55593         return this.selections.length;
55594     },
55595
55596     /**
55597      * Selects the first row in the grid.
55598      */
55599     selectFirstRow : function(){
55600         this.selectRow(0);
55601     },
55602
55603     /**
55604      * Select the last row.
55605      * @param {Boolean} keepExisting (optional) True to keep existing selections
55606      */
55607     selectLastRow : function(keepExisting){
55608         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55609     },
55610
55611     /**
55612      * Selects the row immediately following the last selected row.
55613      * @param {Boolean} keepExisting (optional) True to keep existing selections
55614      */
55615     selectNext : function(keepExisting){
55616         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55617             this.selectRow(this.last+1, keepExisting);
55618             this.grid.getView().focusRow(this.last);
55619         }
55620     },
55621
55622     /**
55623      * Selects the row that precedes the last selected row.
55624      * @param {Boolean} keepExisting (optional) True to keep existing selections
55625      */
55626     selectPrevious : function(keepExisting){
55627         if(this.last){
55628             this.selectRow(this.last-1, keepExisting);
55629             this.grid.getView().focusRow(this.last);
55630         }
55631     },
55632
55633     /**
55634      * Returns the selected records
55635      * @return {Array} Array of selected records
55636      */
55637     getSelections : function(){
55638         return [].concat(this.selections.items);
55639     },
55640
55641     /**
55642      * Returns the first selected record.
55643      * @return {Record}
55644      */
55645     getSelected : function(){
55646         return this.selections.itemAt(0);
55647     },
55648
55649
55650     /**
55651      * Clears all selections.
55652      */
55653     clearSelections : function(fast){
55654         if(this.locked) return;
55655         if(fast !== true){
55656             var ds = this.grid.dataSource;
55657             var s = this.selections;
55658             s.each(function(r){
55659                 this.deselectRow(ds.indexOfId(r.id));
55660             }, this);
55661             s.clear();
55662         }else{
55663             this.selections.clear();
55664         }
55665         this.last = false;
55666     },
55667
55668
55669     /**
55670      * Selects all rows.
55671      */
55672     selectAll : function(){
55673         if(this.locked) return;
55674         this.selections.clear();
55675         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55676             this.selectRow(i, true);
55677         }
55678     },
55679
55680     /**
55681      * Returns True if there is a selection.
55682      * @return {Boolean}
55683      */
55684     hasSelection : function(){
55685         return this.selections.length > 0;
55686     },
55687
55688     /**
55689      * Returns True if the specified row is selected.
55690      * @param {Number/Record} record The record or index of the record to check
55691      * @return {Boolean}
55692      */
55693     isSelected : function(index){
55694         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55695         return (r && this.selections.key(r.id) ? true : false);
55696     },
55697
55698     /**
55699      * Returns True if the specified record id is selected.
55700      * @param {String} id The id of record to check
55701      * @return {Boolean}
55702      */
55703     isIdSelected : function(id){
55704         return (this.selections.key(id) ? true : false);
55705     },
55706
55707     // private
55708     handleMouseDown : function(e, t){
55709         var view = this.grid.getView(), rowIndex;
55710         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55711             return;
55712         };
55713         if(e.shiftKey && this.last !== false){
55714             var last = this.last;
55715             this.selectRange(last, rowIndex, e.ctrlKey);
55716             this.last = last; // reset the last
55717             view.focusRow(rowIndex);
55718         }else{
55719             var isSelected = this.isSelected(rowIndex);
55720             if(e.button !== 0 && isSelected){
55721                 view.focusRow(rowIndex);
55722             }else if(e.ctrlKey && isSelected){
55723                 this.deselectRow(rowIndex);
55724             }else if(!isSelected){
55725                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55726                 view.focusRow(rowIndex);
55727             }
55728         }
55729         this.fireEvent("afterselectionchange", this);
55730     },
55731     // private
55732     handleDragableRowClick :  function(grid, rowIndex, e) 
55733     {
55734         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55735             this.selectRow(rowIndex, false);
55736             grid.view.focusRow(rowIndex);
55737              this.fireEvent("afterselectionchange", this);
55738         }
55739     },
55740     
55741     /**
55742      * Selects multiple rows.
55743      * @param {Array} rows Array of the indexes of the row to select
55744      * @param {Boolean} keepExisting (optional) True to keep existing selections
55745      */
55746     selectRows : function(rows, keepExisting){
55747         if(!keepExisting){
55748             this.clearSelections();
55749         }
55750         for(var i = 0, len = rows.length; i < len; i++){
55751             this.selectRow(rows[i], true);
55752         }
55753     },
55754
55755     /**
55756      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55757      * @param {Number} startRow The index of the first row in the range
55758      * @param {Number} endRow The index of the last row in the range
55759      * @param {Boolean} keepExisting (optional) True to retain existing selections
55760      */
55761     selectRange : function(startRow, endRow, keepExisting){
55762         if(this.locked) return;
55763         if(!keepExisting){
55764             this.clearSelections();
55765         }
55766         if(startRow <= endRow){
55767             for(var i = startRow; i <= endRow; i++){
55768                 this.selectRow(i, true);
55769             }
55770         }else{
55771             for(var i = startRow; i >= endRow; i--){
55772                 this.selectRow(i, true);
55773             }
55774         }
55775     },
55776
55777     /**
55778      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55779      * @param {Number} startRow The index of the first row in the range
55780      * @param {Number} endRow The index of the last row in the range
55781      */
55782     deselectRange : function(startRow, endRow, preventViewNotify){
55783         if(this.locked) return;
55784         for(var i = startRow; i <= endRow; i++){
55785             this.deselectRow(i, preventViewNotify);
55786         }
55787     },
55788
55789     /**
55790      * Selects a row.
55791      * @param {Number} row The index of the row to select
55792      * @param {Boolean} keepExisting (optional) True to keep existing selections
55793      */
55794     selectRow : function(index, keepExisting, preventViewNotify){
55795         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55796         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55797             if(!keepExisting || this.singleSelect){
55798                 this.clearSelections();
55799             }
55800             var r = this.grid.dataSource.getAt(index);
55801             this.selections.add(r);
55802             this.last = this.lastActive = index;
55803             if(!preventViewNotify){
55804                 this.grid.getView().onRowSelect(index);
55805             }
55806             this.fireEvent("rowselect", this, index, r);
55807             this.fireEvent("selectionchange", this);
55808         }
55809     },
55810
55811     /**
55812      * Deselects a row.
55813      * @param {Number} row The index of the row to deselect
55814      */
55815     deselectRow : function(index, preventViewNotify){
55816         if(this.locked) return;
55817         if(this.last == index){
55818             this.last = false;
55819         }
55820         if(this.lastActive == index){
55821             this.lastActive = false;
55822         }
55823         var r = this.grid.dataSource.getAt(index);
55824         this.selections.remove(r);
55825         if(!preventViewNotify){
55826             this.grid.getView().onRowDeselect(index);
55827         }
55828         this.fireEvent("rowdeselect", this, index);
55829         this.fireEvent("selectionchange", this);
55830     },
55831
55832     // private
55833     restoreLast : function(){
55834         if(this._last){
55835             this.last = this._last;
55836         }
55837     },
55838
55839     // private
55840     acceptsNav : function(row, col, cm){
55841         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55842     },
55843
55844     // private
55845     onEditorKey : function(field, e){
55846         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55847         if(k == e.TAB){
55848             e.stopEvent();
55849             ed.completeEdit();
55850             if(e.shiftKey){
55851                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55852             }else{
55853                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55854             }
55855         }else if(k == e.ENTER && !e.ctrlKey){
55856             e.stopEvent();
55857             ed.completeEdit();
55858             if(e.shiftKey){
55859                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55860             }else{
55861                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55862             }
55863         }else if(k == e.ESC){
55864             ed.cancelEdit();
55865         }
55866         if(newCell){
55867             g.startEditing(newCell[0], newCell[1]);
55868         }
55869     }
55870 });/*
55871  * Based on:
55872  * Ext JS Library 1.1.1
55873  * Copyright(c) 2006-2007, Ext JS, LLC.
55874  *
55875  * Originally Released Under LGPL - original licence link has changed is not relivant.
55876  *
55877  * Fork - LGPL
55878  * <script type="text/javascript">
55879  */
55880 /**
55881  * @class Roo.grid.CellSelectionModel
55882  * @extends Roo.grid.AbstractSelectionModel
55883  * This class provides the basic implementation for cell selection in a grid.
55884  * @constructor
55885  * @param {Object} config The object containing the configuration of this model.
55886  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55887  */
55888 Roo.grid.CellSelectionModel = function(config){
55889     Roo.apply(this, config);
55890
55891     this.selection = null;
55892
55893     this.addEvents({
55894         /**
55895              * @event beforerowselect
55896              * Fires before a cell is selected.
55897              * @param {SelectionModel} this
55898              * @param {Number} rowIndex The selected row index
55899              * @param {Number} colIndex The selected cell index
55900              */
55901             "beforecellselect" : true,
55902         /**
55903              * @event cellselect
55904              * Fires when a cell is selected.
55905              * @param {SelectionModel} this
55906              * @param {Number} rowIndex The selected row index
55907              * @param {Number} colIndex The selected cell index
55908              */
55909             "cellselect" : true,
55910         /**
55911              * @event selectionchange
55912              * Fires when the active selection changes.
55913              * @param {SelectionModel} this
55914              * @param {Object} selection null for no selection or an object (o) with two properties
55915                 <ul>
55916                 <li>o.record: the record object for the row the selection is in</li>
55917                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55918                 </ul>
55919              */
55920             "selectionchange" : true,
55921         /**
55922              * @event tabend
55923              * Fires when the tab (or enter) was pressed on the last editable cell
55924              * You can use this to trigger add new row.
55925              * @param {SelectionModel} this
55926              */
55927             "tabend" : true,
55928          /**
55929              * @event beforeeditnext
55930              * Fires before the next editable sell is made active
55931              * You can use this to skip to another cell or fire the tabend
55932              *    if you set cell to false
55933              * @param {Object} eventdata object : { cell : [ row, col ] } 
55934              */
55935             "beforeeditnext" : true
55936     });
55937     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55938 };
55939
55940 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55941     
55942     enter_is_tab: false,
55943
55944     /** @ignore */
55945     initEvents : function(){
55946         this.grid.on("mousedown", this.handleMouseDown, this);
55947         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55948         var view = this.grid.view;
55949         view.on("refresh", this.onViewChange, this);
55950         view.on("rowupdated", this.onRowUpdated, this);
55951         view.on("beforerowremoved", this.clearSelections, this);
55952         view.on("beforerowsinserted", this.clearSelections, this);
55953         if(this.grid.isEditor){
55954             this.grid.on("beforeedit", this.beforeEdit,  this);
55955         }
55956     },
55957
55958         //private
55959     beforeEdit : function(e){
55960         this.select(e.row, e.column, false, true, e.record);
55961     },
55962
55963         //private
55964     onRowUpdated : function(v, index, r){
55965         if(this.selection && this.selection.record == r){
55966             v.onCellSelect(index, this.selection.cell[1]);
55967         }
55968     },
55969
55970         //private
55971     onViewChange : function(){
55972         this.clearSelections(true);
55973     },
55974
55975         /**
55976          * Returns the currently selected cell,.
55977          * @return {Array} The selected cell (row, column) or null if none selected.
55978          */
55979     getSelectedCell : function(){
55980         return this.selection ? this.selection.cell : null;
55981     },
55982
55983     /**
55984      * Clears all selections.
55985      * @param {Boolean} true to prevent the gridview from being notified about the change.
55986      */
55987     clearSelections : function(preventNotify){
55988         var s = this.selection;
55989         if(s){
55990             if(preventNotify !== true){
55991                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55992             }
55993             this.selection = null;
55994             this.fireEvent("selectionchange", this, null);
55995         }
55996     },
55997
55998     /**
55999      * Returns true if there is a selection.
56000      * @return {Boolean}
56001      */
56002     hasSelection : function(){
56003         return this.selection ? true : false;
56004     },
56005
56006     /** @ignore */
56007     handleMouseDown : function(e, t){
56008         var v = this.grid.getView();
56009         if(this.isLocked()){
56010             return;
56011         };
56012         var row = v.findRowIndex(t);
56013         var cell = v.findCellIndex(t);
56014         if(row !== false && cell !== false){
56015             this.select(row, cell);
56016         }
56017     },
56018
56019     /**
56020      * Selects a cell.
56021      * @param {Number} rowIndex
56022      * @param {Number} collIndex
56023      */
56024     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56025         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56026             this.clearSelections();
56027             r = r || this.grid.dataSource.getAt(rowIndex);
56028             this.selection = {
56029                 record : r,
56030                 cell : [rowIndex, colIndex]
56031             };
56032             if(!preventViewNotify){
56033                 var v = this.grid.getView();
56034                 v.onCellSelect(rowIndex, colIndex);
56035                 if(preventFocus !== true){
56036                     v.focusCell(rowIndex, colIndex);
56037                 }
56038             }
56039             this.fireEvent("cellselect", this, rowIndex, colIndex);
56040             this.fireEvent("selectionchange", this, this.selection);
56041         }
56042     },
56043
56044         //private
56045     isSelectable : function(rowIndex, colIndex, cm){
56046         return !cm.isHidden(colIndex);
56047     },
56048
56049     /** @ignore */
56050     handleKeyDown : function(e){
56051         //Roo.log('Cell Sel Model handleKeyDown');
56052         if(!e.isNavKeyPress()){
56053             return;
56054         }
56055         var g = this.grid, s = this.selection;
56056         if(!s){
56057             e.stopEvent();
56058             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56059             if(cell){
56060                 this.select(cell[0], cell[1]);
56061             }
56062             return;
56063         }
56064         var sm = this;
56065         var walk = function(row, col, step){
56066             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56067         };
56068         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56069         var newCell;
56070
56071       
56072
56073         switch(k){
56074             case e.TAB:
56075                 // handled by onEditorKey
56076                 if (g.isEditor && g.editing) {
56077                     return;
56078                 }
56079                 if(e.shiftKey) {
56080                     newCell = walk(r, c-1, -1);
56081                 } else {
56082                     newCell = walk(r, c+1, 1);
56083                 }
56084                 break;
56085             
56086             case e.DOWN:
56087                newCell = walk(r+1, c, 1);
56088                 break;
56089             
56090             case e.UP:
56091                 newCell = walk(r-1, c, -1);
56092                 break;
56093             
56094             case e.RIGHT:
56095                 newCell = walk(r, c+1, 1);
56096                 break;
56097             
56098             case e.LEFT:
56099                 newCell = walk(r, c-1, -1);
56100                 break;
56101             
56102             case e.ENTER:
56103                 
56104                 if(g.isEditor && !g.editing){
56105                    g.startEditing(r, c);
56106                    e.stopEvent();
56107                    return;
56108                 }
56109                 
56110                 
56111              break;
56112         };
56113         if(newCell){
56114             this.select(newCell[0], newCell[1]);
56115             e.stopEvent();
56116             
56117         }
56118     },
56119
56120     acceptsNav : function(row, col, cm){
56121         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56122     },
56123     /**
56124      * Selects a cell.
56125      * @param {Number} field (not used) - as it's normally used as a listener
56126      * @param {Number} e - event - fake it by using
56127      *
56128      * var e = Roo.EventObjectImpl.prototype;
56129      * e.keyCode = e.TAB
56130      *
56131      * 
56132      */
56133     onEditorKey : function(field, e){
56134         
56135         var k = e.getKey(),
56136             newCell,
56137             g = this.grid,
56138             ed = g.activeEditor,
56139             forward = false;
56140         ///Roo.log('onEditorKey' + k);
56141         
56142         
56143         if (this.enter_is_tab && k == e.ENTER) {
56144             k = e.TAB;
56145         }
56146         
56147         if(k == e.TAB){
56148             if(e.shiftKey){
56149                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56150             }else{
56151                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56152                 forward = true;
56153             }
56154             
56155             e.stopEvent();
56156             
56157         } else if(k == e.ENTER &&  !e.ctrlKey){
56158             ed.completeEdit();
56159             e.stopEvent();
56160             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56161         
56162                 } else if(k == e.ESC){
56163             ed.cancelEdit();
56164         }
56165                 
56166         if (newCell) {
56167             var ecall = { cell : newCell, forward : forward };
56168             this.fireEvent('beforeeditnext', ecall );
56169             newCell = ecall.cell;
56170                         forward = ecall.forward;
56171         }
56172                 
56173         if(newCell){
56174             //Roo.log('next cell after edit');
56175             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56176         } else if (forward) {
56177             // tabbed past last
56178             this.fireEvent.defer(100, this, ['tabend',this]);
56179         }
56180     }
56181 });/*
56182  * Based on:
56183  * Ext JS Library 1.1.1
56184  * Copyright(c) 2006-2007, Ext JS, LLC.
56185  *
56186  * Originally Released Under LGPL - original licence link has changed is not relivant.
56187  *
56188  * Fork - LGPL
56189  * <script type="text/javascript">
56190  */
56191  
56192 /**
56193  * @class Roo.grid.EditorGrid
56194  * @extends Roo.grid.Grid
56195  * Class for creating and editable grid.
56196  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56197  * The container MUST have some type of size defined for the grid to fill. The container will be 
56198  * automatically set to position relative if it isn't already.
56199  * @param {Object} dataSource The data model to bind to
56200  * @param {Object} colModel The column model with info about this grid's columns
56201  */
56202 Roo.grid.EditorGrid = function(container, config){
56203     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56204     this.getGridEl().addClass("xedit-grid");
56205
56206     if(!this.selModel){
56207         this.selModel = new Roo.grid.CellSelectionModel();
56208     }
56209
56210     this.activeEditor = null;
56211
56212         this.addEvents({
56213             /**
56214              * @event beforeedit
56215              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56216              * <ul style="padding:5px;padding-left:16px;">
56217              * <li>grid - This grid</li>
56218              * <li>record - The record being edited</li>
56219              * <li>field - The field name being edited</li>
56220              * <li>value - The value for the field being edited.</li>
56221              * <li>row - The grid row index</li>
56222              * <li>column - The grid column index</li>
56223              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56224              * </ul>
56225              * @param {Object} e An edit event (see above for description)
56226              */
56227             "beforeedit" : true,
56228             /**
56229              * @event afteredit
56230              * Fires after a cell is edited. <br />
56231              * <ul style="padding:5px;padding-left:16px;">
56232              * <li>grid - This grid</li>
56233              * <li>record - The record being edited</li>
56234              * <li>field - The field name being edited</li>
56235              * <li>value - The value being set</li>
56236              * <li>originalValue - The original value for the field, before the edit.</li>
56237              * <li>row - The grid row index</li>
56238              * <li>column - The grid column index</li>
56239              * </ul>
56240              * @param {Object} e An edit event (see above for description)
56241              */
56242             "afteredit" : true,
56243             /**
56244              * @event validateedit
56245              * Fires after a cell is edited, but before the value is set in the record. 
56246          * You can use this to modify the value being set in the field, Return false
56247              * to cancel the change. The edit event object has the following properties <br />
56248              * <ul style="padding:5px;padding-left:16px;">
56249          * <li>editor - This editor</li>
56250              * <li>grid - This grid</li>
56251              * <li>record - The record being edited</li>
56252              * <li>field - The field name being edited</li>
56253              * <li>value - The value being set</li>
56254              * <li>originalValue - The original value for the field, before the edit.</li>
56255              * <li>row - The grid row index</li>
56256              * <li>column - The grid column index</li>
56257              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56258              * </ul>
56259              * @param {Object} e An edit event (see above for description)
56260              */
56261             "validateedit" : true
56262         });
56263     this.on("bodyscroll", this.stopEditing,  this);
56264     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56265 };
56266
56267 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56268     /**
56269      * @cfg {Number} clicksToEdit
56270      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56271      */
56272     clicksToEdit: 2,
56273
56274     // private
56275     isEditor : true,
56276     // private
56277     trackMouseOver: false, // causes very odd FF errors
56278
56279     onCellDblClick : function(g, row, col){
56280         this.startEditing(row, col);
56281     },
56282
56283     onEditComplete : function(ed, value, startValue){
56284         this.editing = false;
56285         this.activeEditor = null;
56286         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56287         var r = ed.record;
56288         var field = this.colModel.getDataIndex(ed.col);
56289         var e = {
56290             grid: this,
56291             record: r,
56292             field: field,
56293             originalValue: startValue,
56294             value: value,
56295             row: ed.row,
56296             column: ed.col,
56297             cancel:false,
56298             editor: ed
56299         };
56300         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56301         cell.show();
56302           
56303         if(String(value) !== String(startValue)){
56304             
56305             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56306                 r.set(field, e.value);
56307                 // if we are dealing with a combo box..
56308                 // then we also set the 'name' colum to be the displayField
56309                 if (ed.field.displayField && ed.field.name) {
56310                     r.set(ed.field.name, ed.field.el.dom.value);
56311                 }
56312                 
56313                 delete e.cancel; //?? why!!!
56314                 this.fireEvent("afteredit", e);
56315             }
56316         } else {
56317             this.fireEvent("afteredit", e); // always fire it!
56318         }
56319         this.view.focusCell(ed.row, ed.col);
56320     },
56321
56322     /**
56323      * Starts editing the specified for the specified row/column
56324      * @param {Number} rowIndex
56325      * @param {Number} colIndex
56326      */
56327     startEditing : function(row, col){
56328         this.stopEditing();
56329         if(this.colModel.isCellEditable(col, row)){
56330             this.view.ensureVisible(row, col, true);
56331           
56332             var r = this.dataSource.getAt(row);
56333             var field = this.colModel.getDataIndex(col);
56334             var cell = Roo.get(this.view.getCell(row,col));
56335             var e = {
56336                 grid: this,
56337                 record: r,
56338                 field: field,
56339                 value: r.data[field],
56340                 row: row,
56341                 column: col,
56342                 cancel:false 
56343             };
56344             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56345                 this.editing = true;
56346                 var ed = this.colModel.getCellEditor(col, row);
56347                 
56348                 if (!ed) {
56349                     return;
56350                 }
56351                 if(!ed.rendered){
56352                     ed.render(ed.parentEl || document.body);
56353                 }
56354                 ed.field.reset();
56355                
56356                 cell.hide();
56357                 
56358                 (function(){ // complex but required for focus issues in safari, ie and opera
56359                     ed.row = row;
56360                     ed.col = col;
56361                     ed.record = r;
56362                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56363                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56364                     this.activeEditor = ed;
56365                     var v = r.data[field];
56366                     ed.startEdit(this.view.getCell(row, col), v);
56367                     // combo's with 'displayField and name set
56368                     if (ed.field.displayField && ed.field.name) {
56369                         ed.field.el.dom.value = r.data[ed.field.name];
56370                     }
56371                     
56372                     
56373                 }).defer(50, this);
56374             }
56375         }
56376     },
56377         
56378     /**
56379      * Stops any active editing
56380      */
56381     stopEditing : function(){
56382         if(this.activeEditor){
56383             this.activeEditor.completeEdit();
56384         }
56385         this.activeEditor = null;
56386     },
56387         
56388          /**
56389      * Called to get grid's drag proxy text, by default returns this.ddText.
56390      * @return {String}
56391      */
56392     getDragDropText : function(){
56393         var count = this.selModel.getSelectedCell() ? 1 : 0;
56394         return String.format(this.ddText, count, count == 1 ? '' : 's');
56395     }
56396         
56397 });/*
56398  * Based on:
56399  * Ext JS Library 1.1.1
56400  * Copyright(c) 2006-2007, Ext JS, LLC.
56401  *
56402  * Originally Released Under LGPL - original licence link has changed is not relivant.
56403  *
56404  * Fork - LGPL
56405  * <script type="text/javascript">
56406  */
56407
56408 // private - not really -- you end up using it !
56409 // This is a support class used internally by the Grid components
56410
56411 /**
56412  * @class Roo.grid.GridEditor
56413  * @extends Roo.Editor
56414  * Class for creating and editable grid elements.
56415  * @param {Object} config any settings (must include field)
56416  */
56417 Roo.grid.GridEditor = function(field, config){
56418     if (!config && field.field) {
56419         config = field;
56420         field = Roo.factory(config.field, Roo.form);
56421     }
56422     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56423     field.monitorTab = false;
56424 };
56425
56426 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56427     
56428     /**
56429      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56430      */
56431     
56432     alignment: "tl-tl",
56433     autoSize: "width",
56434     hideEl : false,
56435     cls: "x-small-editor x-grid-editor",
56436     shim:false,
56437     shadow:"frame"
56438 });/*
56439  * Based on:
56440  * Ext JS Library 1.1.1
56441  * Copyright(c) 2006-2007, Ext JS, LLC.
56442  *
56443  * Originally Released Under LGPL - original licence link has changed is not relivant.
56444  *
56445  * Fork - LGPL
56446  * <script type="text/javascript">
56447  */
56448   
56449
56450   
56451 Roo.grid.PropertyRecord = Roo.data.Record.create([
56452     {name:'name',type:'string'},  'value'
56453 ]);
56454
56455
56456 Roo.grid.PropertyStore = function(grid, source){
56457     this.grid = grid;
56458     this.store = new Roo.data.Store({
56459         recordType : Roo.grid.PropertyRecord
56460     });
56461     this.store.on('update', this.onUpdate,  this);
56462     if(source){
56463         this.setSource(source);
56464     }
56465     Roo.grid.PropertyStore.superclass.constructor.call(this);
56466 };
56467
56468
56469
56470 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56471     setSource : function(o){
56472         this.source = o;
56473         this.store.removeAll();
56474         var data = [];
56475         for(var k in o){
56476             if(this.isEditableValue(o[k])){
56477                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56478             }
56479         }
56480         this.store.loadRecords({records: data}, {}, true);
56481     },
56482
56483     onUpdate : function(ds, record, type){
56484         if(type == Roo.data.Record.EDIT){
56485             var v = record.data['value'];
56486             var oldValue = record.modified['value'];
56487             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56488                 this.source[record.id] = v;
56489                 record.commit();
56490                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56491             }else{
56492                 record.reject();
56493             }
56494         }
56495     },
56496
56497     getProperty : function(row){
56498        return this.store.getAt(row);
56499     },
56500
56501     isEditableValue: function(val){
56502         if(val && val instanceof Date){
56503             return true;
56504         }else if(typeof val == 'object' || typeof val == 'function'){
56505             return false;
56506         }
56507         return true;
56508     },
56509
56510     setValue : function(prop, value){
56511         this.source[prop] = value;
56512         this.store.getById(prop).set('value', value);
56513     },
56514
56515     getSource : function(){
56516         return this.source;
56517     }
56518 });
56519
56520 Roo.grid.PropertyColumnModel = function(grid, store){
56521     this.grid = grid;
56522     var g = Roo.grid;
56523     g.PropertyColumnModel.superclass.constructor.call(this, [
56524         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56525         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56526     ]);
56527     this.store = store;
56528     this.bselect = Roo.DomHelper.append(document.body, {
56529         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56530             {tag: 'option', value: 'true', html: 'true'},
56531             {tag: 'option', value: 'false', html: 'false'}
56532         ]
56533     });
56534     Roo.id(this.bselect);
56535     var f = Roo.form;
56536     this.editors = {
56537         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56538         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56539         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56540         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56541         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56542     };
56543     this.renderCellDelegate = this.renderCell.createDelegate(this);
56544     this.renderPropDelegate = this.renderProp.createDelegate(this);
56545 };
56546
56547 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56548     
56549     
56550     nameText : 'Name',
56551     valueText : 'Value',
56552     
56553     dateFormat : 'm/j/Y',
56554     
56555     
56556     renderDate : function(dateVal){
56557         return dateVal.dateFormat(this.dateFormat);
56558     },
56559
56560     renderBool : function(bVal){
56561         return bVal ? 'true' : 'false';
56562     },
56563
56564     isCellEditable : function(colIndex, rowIndex){
56565         return colIndex == 1;
56566     },
56567
56568     getRenderer : function(col){
56569         return col == 1 ?
56570             this.renderCellDelegate : this.renderPropDelegate;
56571     },
56572
56573     renderProp : function(v){
56574         return this.getPropertyName(v);
56575     },
56576
56577     renderCell : function(val){
56578         var rv = val;
56579         if(val instanceof Date){
56580             rv = this.renderDate(val);
56581         }else if(typeof val == 'boolean'){
56582             rv = this.renderBool(val);
56583         }
56584         return Roo.util.Format.htmlEncode(rv);
56585     },
56586
56587     getPropertyName : function(name){
56588         var pn = this.grid.propertyNames;
56589         return pn && pn[name] ? pn[name] : name;
56590     },
56591
56592     getCellEditor : function(colIndex, rowIndex){
56593         var p = this.store.getProperty(rowIndex);
56594         var n = p.data['name'], val = p.data['value'];
56595         
56596         if(typeof(this.grid.customEditors[n]) == 'string'){
56597             return this.editors[this.grid.customEditors[n]];
56598         }
56599         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56600             return this.grid.customEditors[n];
56601         }
56602         if(val instanceof Date){
56603             return this.editors['date'];
56604         }else if(typeof val == 'number'){
56605             return this.editors['number'];
56606         }else if(typeof val == 'boolean'){
56607             return this.editors['boolean'];
56608         }else{
56609             return this.editors['string'];
56610         }
56611     }
56612 });
56613
56614 /**
56615  * @class Roo.grid.PropertyGrid
56616  * @extends Roo.grid.EditorGrid
56617  * This class represents the  interface of a component based property grid control.
56618  * <br><br>Usage:<pre><code>
56619  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56620       
56621  });
56622  // set any options
56623  grid.render();
56624  * </code></pre>
56625   
56626  * @constructor
56627  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56628  * The container MUST have some type of size defined for the grid to fill. The container will be
56629  * automatically set to position relative if it isn't already.
56630  * @param {Object} config A config object that sets properties on this grid.
56631  */
56632 Roo.grid.PropertyGrid = function(container, config){
56633     config = config || {};
56634     var store = new Roo.grid.PropertyStore(this);
56635     this.store = store;
56636     var cm = new Roo.grid.PropertyColumnModel(this, store);
56637     store.store.sort('name', 'ASC');
56638     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56639         ds: store.store,
56640         cm: cm,
56641         enableColLock:false,
56642         enableColumnMove:false,
56643         stripeRows:false,
56644         trackMouseOver: false,
56645         clicksToEdit:1
56646     }, config));
56647     this.getGridEl().addClass('x-props-grid');
56648     this.lastEditRow = null;
56649     this.on('columnresize', this.onColumnResize, this);
56650     this.addEvents({
56651          /**
56652              * @event beforepropertychange
56653              * Fires before a property changes (return false to stop?)
56654              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56655              * @param {String} id Record Id
56656              * @param {String} newval New Value
56657          * @param {String} oldval Old Value
56658              */
56659         "beforepropertychange": true,
56660         /**
56661              * @event propertychange
56662              * Fires after a property changes
56663              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56664              * @param {String} id Record Id
56665              * @param {String} newval New Value
56666          * @param {String} oldval Old Value
56667              */
56668         "propertychange": true
56669     });
56670     this.customEditors = this.customEditors || {};
56671 };
56672 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56673     
56674      /**
56675      * @cfg {Object} customEditors map of colnames=> custom editors.
56676      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56677      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56678      * false disables editing of the field.
56679          */
56680     
56681       /**
56682      * @cfg {Object} propertyNames map of property Names to their displayed value
56683          */
56684     
56685     render : function(){
56686         Roo.grid.PropertyGrid.superclass.render.call(this);
56687         this.autoSize.defer(100, this);
56688     },
56689
56690     autoSize : function(){
56691         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56692         if(this.view){
56693             this.view.fitColumns();
56694         }
56695     },
56696
56697     onColumnResize : function(){
56698         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56699         this.autoSize();
56700     },
56701     /**
56702      * Sets the data for the Grid
56703      * accepts a Key => Value object of all the elements avaiable.
56704      * @param {Object} data  to appear in grid.
56705      */
56706     setSource : function(source){
56707         this.store.setSource(source);
56708         //this.autoSize();
56709     },
56710     /**
56711      * Gets all the data from the grid.
56712      * @return {Object} data  data stored in grid
56713      */
56714     getSource : function(){
56715         return this.store.getSource();
56716     }
56717 });/*
56718   
56719  * Licence LGPL
56720  
56721  */
56722  
56723 /**
56724  * @class Roo.grid.Calendar
56725  * @extends Roo.util.Grid
56726  * This class extends the Grid to provide a calendar widget
56727  * <br><br>Usage:<pre><code>
56728  var grid = new Roo.grid.Calendar("my-container-id", {
56729      ds: myDataStore,
56730      cm: myColModel,
56731      selModel: mySelectionModel,
56732      autoSizeColumns: true,
56733      monitorWindowResize: false,
56734      trackMouseOver: true
56735      eventstore : real data store..
56736  });
56737  // set any options
56738  grid.render();
56739   
56740   * @constructor
56741  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56742  * The container MUST have some type of size defined for the grid to fill. The container will be
56743  * automatically set to position relative if it isn't already.
56744  * @param {Object} config A config object that sets properties on this grid.
56745  */
56746 Roo.grid.Calendar = function(container, config){
56747         // initialize the container
56748         this.container = Roo.get(container);
56749         this.container.update("");
56750         this.container.setStyle("overflow", "hidden");
56751     this.container.addClass('x-grid-container');
56752
56753     this.id = this.container.id;
56754
56755     Roo.apply(this, config);
56756     // check and correct shorthanded configs
56757     
56758     var rows = [];
56759     var d =1;
56760     for (var r = 0;r < 6;r++) {
56761         
56762         rows[r]=[];
56763         for (var c =0;c < 7;c++) {
56764             rows[r][c]= '';
56765         }
56766     }
56767     if (this.eventStore) {
56768         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56769         this.eventStore.on('load',this.onLoad, this);
56770         this.eventStore.on('beforeload',this.clearEvents, this);
56771          
56772     }
56773     
56774     this.dataSource = new Roo.data.Store({
56775             proxy: new Roo.data.MemoryProxy(rows),
56776             reader: new Roo.data.ArrayReader({}, [
56777                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56778     });
56779
56780     this.dataSource.load();
56781     this.ds = this.dataSource;
56782     this.ds.xmodule = this.xmodule || false;
56783     
56784     
56785     var cellRender = function(v,x,r)
56786     {
56787         return String.format(
56788             '<div class="fc-day  fc-widget-content"><div>' +
56789                 '<div class="fc-event-container"></div>' +
56790                 '<div class="fc-day-number">{0}</div>'+
56791                 
56792                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56793             '</div></div>', v);
56794     
56795     }
56796     
56797     
56798     this.colModel = new Roo.grid.ColumnModel( [
56799         {
56800             xtype: 'ColumnModel',
56801             xns: Roo.grid,
56802             dataIndex : 'weekday0',
56803             header : 'Sunday',
56804             renderer : cellRender
56805         },
56806         {
56807             xtype: 'ColumnModel',
56808             xns: Roo.grid,
56809             dataIndex : 'weekday1',
56810             header : 'Monday',
56811             renderer : cellRender
56812         },
56813         {
56814             xtype: 'ColumnModel',
56815             xns: Roo.grid,
56816             dataIndex : 'weekday2',
56817             header : 'Tuesday',
56818             renderer : cellRender
56819         },
56820         {
56821             xtype: 'ColumnModel',
56822             xns: Roo.grid,
56823             dataIndex : 'weekday3',
56824             header : 'Wednesday',
56825             renderer : cellRender
56826         },
56827         {
56828             xtype: 'ColumnModel',
56829             xns: Roo.grid,
56830             dataIndex : 'weekday4',
56831             header : 'Thursday',
56832             renderer : cellRender
56833         },
56834         {
56835             xtype: 'ColumnModel',
56836             xns: Roo.grid,
56837             dataIndex : 'weekday5',
56838             header : 'Friday',
56839             renderer : cellRender
56840         },
56841         {
56842             xtype: 'ColumnModel',
56843             xns: Roo.grid,
56844             dataIndex : 'weekday6',
56845             header : 'Saturday',
56846             renderer : cellRender
56847         }
56848     ]);
56849     this.cm = this.colModel;
56850     this.cm.xmodule = this.xmodule || false;
56851  
56852         
56853           
56854     //this.selModel = new Roo.grid.CellSelectionModel();
56855     //this.sm = this.selModel;
56856     //this.selModel.init(this);
56857     
56858     
56859     if(this.width){
56860         this.container.setWidth(this.width);
56861     }
56862
56863     if(this.height){
56864         this.container.setHeight(this.height);
56865     }
56866     /** @private */
56867         this.addEvents({
56868         // raw events
56869         /**
56870          * @event click
56871          * The raw click event for the entire grid.
56872          * @param {Roo.EventObject} e
56873          */
56874         "click" : true,
56875         /**
56876          * @event dblclick
56877          * The raw dblclick event for the entire grid.
56878          * @param {Roo.EventObject} e
56879          */
56880         "dblclick" : true,
56881         /**
56882          * @event contextmenu
56883          * The raw contextmenu event for the entire grid.
56884          * @param {Roo.EventObject} e
56885          */
56886         "contextmenu" : true,
56887         /**
56888          * @event mousedown
56889          * The raw mousedown event for the entire grid.
56890          * @param {Roo.EventObject} e
56891          */
56892         "mousedown" : true,
56893         /**
56894          * @event mouseup
56895          * The raw mouseup event for the entire grid.
56896          * @param {Roo.EventObject} e
56897          */
56898         "mouseup" : true,
56899         /**
56900          * @event mouseover
56901          * The raw mouseover event for the entire grid.
56902          * @param {Roo.EventObject} e
56903          */
56904         "mouseover" : true,
56905         /**
56906          * @event mouseout
56907          * The raw mouseout event for the entire grid.
56908          * @param {Roo.EventObject} e
56909          */
56910         "mouseout" : true,
56911         /**
56912          * @event keypress
56913          * The raw keypress event for the entire grid.
56914          * @param {Roo.EventObject} e
56915          */
56916         "keypress" : true,
56917         /**
56918          * @event keydown
56919          * The raw keydown event for the entire grid.
56920          * @param {Roo.EventObject} e
56921          */
56922         "keydown" : true,
56923
56924         // custom events
56925
56926         /**
56927          * @event cellclick
56928          * Fires when a cell is clicked
56929          * @param {Grid} this
56930          * @param {Number} rowIndex
56931          * @param {Number} columnIndex
56932          * @param {Roo.EventObject} e
56933          */
56934         "cellclick" : true,
56935         /**
56936          * @event celldblclick
56937          * Fires when a cell is double clicked
56938          * @param {Grid} this
56939          * @param {Number} rowIndex
56940          * @param {Number} columnIndex
56941          * @param {Roo.EventObject} e
56942          */
56943         "celldblclick" : true,
56944         /**
56945          * @event rowclick
56946          * Fires when a row is clicked
56947          * @param {Grid} this
56948          * @param {Number} rowIndex
56949          * @param {Roo.EventObject} e
56950          */
56951         "rowclick" : true,
56952         /**
56953          * @event rowdblclick
56954          * Fires when a row is double clicked
56955          * @param {Grid} this
56956          * @param {Number} rowIndex
56957          * @param {Roo.EventObject} e
56958          */
56959         "rowdblclick" : true,
56960         /**
56961          * @event headerclick
56962          * Fires when a header is clicked
56963          * @param {Grid} this
56964          * @param {Number} columnIndex
56965          * @param {Roo.EventObject} e
56966          */
56967         "headerclick" : true,
56968         /**
56969          * @event headerdblclick
56970          * Fires when a header cell is double clicked
56971          * @param {Grid} this
56972          * @param {Number} columnIndex
56973          * @param {Roo.EventObject} e
56974          */
56975         "headerdblclick" : true,
56976         /**
56977          * @event rowcontextmenu
56978          * Fires when a row is right clicked
56979          * @param {Grid} this
56980          * @param {Number} rowIndex
56981          * @param {Roo.EventObject} e
56982          */
56983         "rowcontextmenu" : true,
56984         /**
56985          * @event cellcontextmenu
56986          * Fires when a cell is right clicked
56987          * @param {Grid} this
56988          * @param {Number} rowIndex
56989          * @param {Number} cellIndex
56990          * @param {Roo.EventObject} e
56991          */
56992          "cellcontextmenu" : true,
56993         /**
56994          * @event headercontextmenu
56995          * Fires when a header is right clicked
56996          * @param {Grid} this
56997          * @param {Number} columnIndex
56998          * @param {Roo.EventObject} e
56999          */
57000         "headercontextmenu" : true,
57001         /**
57002          * @event bodyscroll
57003          * Fires when the body element is scrolled
57004          * @param {Number} scrollLeft
57005          * @param {Number} scrollTop
57006          */
57007         "bodyscroll" : true,
57008         /**
57009          * @event columnresize
57010          * Fires when the user resizes a column
57011          * @param {Number} columnIndex
57012          * @param {Number} newSize
57013          */
57014         "columnresize" : true,
57015         /**
57016          * @event columnmove
57017          * Fires when the user moves a column
57018          * @param {Number} oldIndex
57019          * @param {Number} newIndex
57020          */
57021         "columnmove" : true,
57022         /**
57023          * @event startdrag
57024          * Fires when row(s) start being dragged
57025          * @param {Grid} this
57026          * @param {Roo.GridDD} dd The drag drop object
57027          * @param {event} e The raw browser event
57028          */
57029         "startdrag" : true,
57030         /**
57031          * @event enddrag
57032          * Fires when a drag operation is complete
57033          * @param {Grid} this
57034          * @param {Roo.GridDD} dd The drag drop object
57035          * @param {event} e The raw browser event
57036          */
57037         "enddrag" : true,
57038         /**
57039          * @event dragdrop
57040          * Fires when dragged row(s) are dropped on a valid DD target
57041          * @param {Grid} this
57042          * @param {Roo.GridDD} dd The drag drop object
57043          * @param {String} targetId The target drag drop object
57044          * @param {event} e The raw browser event
57045          */
57046         "dragdrop" : true,
57047         /**
57048          * @event dragover
57049          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57050          * @param {Grid} this
57051          * @param {Roo.GridDD} dd The drag drop object
57052          * @param {String} targetId The target drag drop object
57053          * @param {event} e The raw browser event
57054          */
57055         "dragover" : true,
57056         /**
57057          * @event dragenter
57058          *  Fires when the dragged row(s) first cross another DD target while being dragged
57059          * @param {Grid} this
57060          * @param {Roo.GridDD} dd The drag drop object
57061          * @param {String} targetId The target drag drop object
57062          * @param {event} e The raw browser event
57063          */
57064         "dragenter" : true,
57065         /**
57066          * @event dragout
57067          * Fires when the dragged row(s) leave another DD target while being dragged
57068          * @param {Grid} this
57069          * @param {Roo.GridDD} dd The drag drop object
57070          * @param {String} targetId The target drag drop object
57071          * @param {event} e The raw browser event
57072          */
57073         "dragout" : true,
57074         /**
57075          * @event rowclass
57076          * Fires when a row is rendered, so you can change add a style to it.
57077          * @param {GridView} gridview   The grid view
57078          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57079          */
57080         'rowclass' : true,
57081
57082         /**
57083          * @event render
57084          * Fires when the grid is rendered
57085          * @param {Grid} grid
57086          */
57087         'render' : true,
57088             /**
57089              * @event select
57090              * Fires when a date is selected
57091              * @param {DatePicker} this
57092              * @param {Date} date The selected date
57093              */
57094         'select': true,
57095         /**
57096              * @event monthchange
57097              * Fires when the displayed month changes 
57098              * @param {DatePicker} this
57099              * @param {Date} date The selected month
57100              */
57101         'monthchange': true,
57102         /**
57103              * @event evententer
57104              * Fires when mouse over an event
57105              * @param {Calendar} this
57106              * @param {event} Event
57107              */
57108         'evententer': true,
57109         /**
57110              * @event eventleave
57111              * Fires when the mouse leaves an
57112              * @param {Calendar} this
57113              * @param {event}
57114              */
57115         'eventleave': true,
57116         /**
57117              * @event eventclick
57118              * Fires when the mouse click an
57119              * @param {Calendar} this
57120              * @param {event}
57121              */
57122         'eventclick': true,
57123         /**
57124              * @event eventrender
57125              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57126              * @param {Calendar} this
57127              * @param {data} data to be modified
57128              */
57129         'eventrender': true
57130         
57131     });
57132
57133     Roo.grid.Grid.superclass.constructor.call(this);
57134     this.on('render', function() {
57135         this.view.el.addClass('x-grid-cal'); 
57136         
57137         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57138
57139     },this);
57140     
57141     if (!Roo.grid.Calendar.style) {
57142         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57143             
57144             
57145             '.x-grid-cal .x-grid-col' :  {
57146                 height: 'auto !important',
57147                 'vertical-align': 'top'
57148             },
57149             '.x-grid-cal  .fc-event-hori' : {
57150                 height: '14px'
57151             }
57152              
57153             
57154         }, Roo.id());
57155     }
57156
57157     
57158     
57159 };
57160 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57161     /**
57162      * @cfg {Store} eventStore The store that loads events.
57163      */
57164     eventStore : 25,
57165
57166      
57167     activeDate : false,
57168     startDay : 0,
57169     autoWidth : true,
57170     monitorWindowResize : false,
57171
57172     
57173     resizeColumns : function() {
57174         var col = (this.view.el.getWidth() / 7) - 3;
57175         // loop through cols, and setWidth
57176         for(var i =0 ; i < 7 ; i++){
57177             this.cm.setColumnWidth(i, col);
57178         }
57179     },
57180      setDate :function(date) {
57181         
57182         Roo.log('setDate?');
57183         
57184         this.resizeColumns();
57185         var vd = this.activeDate;
57186         this.activeDate = date;
57187 //        if(vd && this.el){
57188 //            var t = date.getTime();
57189 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57190 //                Roo.log('using add remove');
57191 //                
57192 //                this.fireEvent('monthchange', this, date);
57193 //                
57194 //                this.cells.removeClass("fc-state-highlight");
57195 //                this.cells.each(function(c){
57196 //                   if(c.dateValue == t){
57197 //                       c.addClass("fc-state-highlight");
57198 //                       setTimeout(function(){
57199 //                            try{c.dom.firstChild.focus();}catch(e){}
57200 //                       }, 50);
57201 //                       return false;
57202 //                   }
57203 //                   return true;
57204 //                });
57205 //                return;
57206 //            }
57207 //        }
57208         
57209         var days = date.getDaysInMonth();
57210         
57211         var firstOfMonth = date.getFirstDateOfMonth();
57212         var startingPos = firstOfMonth.getDay()-this.startDay;
57213         
57214         if(startingPos < this.startDay){
57215             startingPos += 7;
57216         }
57217         
57218         var pm = date.add(Date.MONTH, -1);
57219         var prevStart = pm.getDaysInMonth()-startingPos;
57220 //        
57221         
57222         
57223         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57224         
57225         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57226         //this.cells.addClassOnOver('fc-state-hover');
57227         
57228         var cells = this.cells.elements;
57229         var textEls = this.textNodes;
57230         
57231         //Roo.each(cells, function(cell){
57232         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57233         //});
57234         
57235         days += startingPos;
57236
57237         // convert everything to numbers so it's fast
57238         var day = 86400000;
57239         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57240         //Roo.log(d);
57241         //Roo.log(pm);
57242         //Roo.log(prevStart);
57243         
57244         var today = new Date().clearTime().getTime();
57245         var sel = date.clearTime().getTime();
57246         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57247         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57248         var ddMatch = this.disabledDatesRE;
57249         var ddText = this.disabledDatesText;
57250         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57251         var ddaysText = this.disabledDaysText;
57252         var format = this.format;
57253         
57254         var setCellClass = function(cal, cell){
57255             
57256             //Roo.log('set Cell Class');
57257             cell.title = "";
57258             var t = d.getTime();
57259             
57260             //Roo.log(d);
57261             
57262             
57263             cell.dateValue = t;
57264             if(t == today){
57265                 cell.className += " fc-today";
57266                 cell.className += " fc-state-highlight";
57267                 cell.title = cal.todayText;
57268             }
57269             if(t == sel){
57270                 // disable highlight in other month..
57271                 cell.className += " fc-state-highlight";
57272                 
57273             }
57274             // disabling
57275             if(t < min) {
57276                 //cell.className = " fc-state-disabled";
57277                 cell.title = cal.minText;
57278                 return;
57279             }
57280             if(t > max) {
57281                 //cell.className = " fc-state-disabled";
57282                 cell.title = cal.maxText;
57283                 return;
57284             }
57285             if(ddays){
57286                 if(ddays.indexOf(d.getDay()) != -1){
57287                     // cell.title = ddaysText;
57288                    // cell.className = " fc-state-disabled";
57289                 }
57290             }
57291             if(ddMatch && format){
57292                 var fvalue = d.dateFormat(format);
57293                 if(ddMatch.test(fvalue)){
57294                     cell.title = ddText.replace("%0", fvalue);
57295                    cell.className = " fc-state-disabled";
57296                 }
57297             }
57298             
57299             if (!cell.initialClassName) {
57300                 cell.initialClassName = cell.dom.className;
57301             }
57302             
57303             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57304         };
57305
57306         var i = 0;
57307         
57308         for(; i < startingPos; i++) {
57309             cells[i].dayName =  (++prevStart);
57310             Roo.log(textEls[i]);
57311             d.setDate(d.getDate()+1);
57312             
57313             //cells[i].className = "fc-past fc-other-month";
57314             setCellClass(this, cells[i]);
57315         }
57316         
57317         var intDay = 0;
57318         
57319         for(; i < days; i++){
57320             intDay = i - startingPos + 1;
57321             cells[i].dayName =  (intDay);
57322             d.setDate(d.getDate()+1);
57323             
57324             cells[i].className = ''; // "x-date-active";
57325             setCellClass(this, cells[i]);
57326         }
57327         var extraDays = 0;
57328         
57329         for(; i < 42; i++) {
57330             //textEls[i].innerHTML = (++extraDays);
57331             
57332             d.setDate(d.getDate()+1);
57333             cells[i].dayName = (++extraDays);
57334             cells[i].className = "fc-future fc-other-month";
57335             setCellClass(this, cells[i]);
57336         }
57337         
57338         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57339         
57340         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57341         
57342         // this will cause all the cells to mis
57343         var rows= [];
57344         var i =0;
57345         for (var r = 0;r < 6;r++) {
57346             for (var c =0;c < 7;c++) {
57347                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57348             }    
57349         }
57350         
57351         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57352         for(i=0;i<cells.length;i++) {
57353             
57354             this.cells.elements[i].dayName = cells[i].dayName ;
57355             this.cells.elements[i].className = cells[i].className;
57356             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57357             this.cells.elements[i].title = cells[i].title ;
57358             this.cells.elements[i].dateValue = cells[i].dateValue ;
57359         }
57360         
57361         
57362         
57363         
57364         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57365         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57366         
57367         ////if(totalRows != 6){
57368             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57369            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57370        // }
57371         
57372         this.fireEvent('monthchange', this, date);
57373         
57374         
57375     },
57376  /**
57377      * Returns the grid's SelectionModel.
57378      * @return {SelectionModel}
57379      */
57380     getSelectionModel : function(){
57381         if(!this.selModel){
57382             this.selModel = new Roo.grid.CellSelectionModel();
57383         }
57384         return this.selModel;
57385     },
57386
57387     load: function() {
57388         this.eventStore.load()
57389         
57390         
57391         
57392     },
57393     
57394     findCell : function(dt) {
57395         dt = dt.clearTime().getTime();
57396         var ret = false;
57397         this.cells.each(function(c){
57398             //Roo.log("check " +c.dateValue + '?=' + dt);
57399             if(c.dateValue == dt){
57400                 ret = c;
57401                 return false;
57402             }
57403             return true;
57404         });
57405         
57406         return ret;
57407     },
57408     
57409     findCells : function(rec) {
57410         var s = rec.data.start_dt.clone().clearTime().getTime();
57411        // Roo.log(s);
57412         var e= rec.data.end_dt.clone().clearTime().getTime();
57413        // Roo.log(e);
57414         var ret = [];
57415         this.cells.each(function(c){
57416              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57417             
57418             if(c.dateValue > e){
57419                 return ;
57420             }
57421             if(c.dateValue < s){
57422                 return ;
57423             }
57424             ret.push(c);
57425         });
57426         
57427         return ret;    
57428     },
57429     
57430     findBestRow: function(cells)
57431     {
57432         var ret = 0;
57433         
57434         for (var i =0 ; i < cells.length;i++) {
57435             ret  = Math.max(cells[i].rows || 0,ret);
57436         }
57437         return ret;
57438         
57439     },
57440     
57441     
57442     addItem : function(rec)
57443     {
57444         // look for vertical location slot in
57445         var cells = this.findCells(rec);
57446         
57447         rec.row = this.findBestRow(cells);
57448         
57449         // work out the location.
57450         
57451         var crow = false;
57452         var rows = [];
57453         for(var i =0; i < cells.length; i++) {
57454             if (!crow) {
57455                 crow = {
57456                     start : cells[i],
57457                     end :  cells[i]
57458                 };
57459                 continue;
57460             }
57461             if (crow.start.getY() == cells[i].getY()) {
57462                 // on same row.
57463                 crow.end = cells[i];
57464                 continue;
57465             }
57466             // different row.
57467             rows.push(crow);
57468             crow = {
57469                 start: cells[i],
57470                 end : cells[i]
57471             };
57472             
57473         }
57474         
57475         rows.push(crow);
57476         rec.els = [];
57477         rec.rows = rows;
57478         rec.cells = cells;
57479         for (var i = 0; i < cells.length;i++) {
57480             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57481             
57482         }
57483         
57484         
57485     },
57486     
57487     clearEvents: function() {
57488         
57489         if (!this.eventStore.getCount()) {
57490             return;
57491         }
57492         // reset number of rows in cells.
57493         Roo.each(this.cells.elements, function(c){
57494             c.rows = 0;
57495         });
57496         
57497         this.eventStore.each(function(e) {
57498             this.clearEvent(e);
57499         },this);
57500         
57501     },
57502     
57503     clearEvent : function(ev)
57504     {
57505         if (ev.els) {
57506             Roo.each(ev.els, function(el) {
57507                 el.un('mouseenter' ,this.onEventEnter, this);
57508                 el.un('mouseleave' ,this.onEventLeave, this);
57509                 el.remove();
57510             },this);
57511             ev.els = [];
57512         }
57513     },
57514     
57515     
57516     renderEvent : function(ev,ctr) {
57517         if (!ctr) {
57518              ctr = this.view.el.select('.fc-event-container',true).first();
57519         }
57520         
57521          
57522         this.clearEvent(ev);
57523             //code
57524        
57525         
57526         
57527         ev.els = [];
57528         var cells = ev.cells;
57529         var rows = ev.rows;
57530         this.fireEvent('eventrender', this, ev);
57531         
57532         for(var i =0; i < rows.length; i++) {
57533             
57534             cls = '';
57535             if (i == 0) {
57536                 cls += ' fc-event-start';
57537             }
57538             if ((i+1) == rows.length) {
57539                 cls += ' fc-event-end';
57540             }
57541             
57542             //Roo.log(ev.data);
57543             // how many rows should it span..
57544             var cg = this.eventTmpl.append(ctr,Roo.apply({
57545                 fccls : cls
57546                 
57547             }, ev.data) , true);
57548             
57549             
57550             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57551             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57552             cg.on('click', this.onEventClick, this, ev);
57553             
57554             ev.els.push(cg);
57555             
57556             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57557             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57558             //Roo.log(cg);
57559              
57560             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57561             cg.setWidth(ebox.right - sbox.x -2);
57562         }
57563     },
57564     
57565     renderEvents: function()
57566     {   
57567         // first make sure there is enough space..
57568         
57569         if (!this.eventTmpl) {
57570             this.eventTmpl = new Roo.Template(
57571                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57572                     '<div class="fc-event-inner">' +
57573                         '<span class="fc-event-time">{time}</span>' +
57574                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57575                     '</div>' +
57576                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57577                 '</div>'
57578             );
57579                 
57580         }
57581                
57582         
57583         
57584         this.cells.each(function(c) {
57585             //Roo.log(c.select('.fc-day-content div',true).first());
57586             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57587         });
57588         
57589         var ctr = this.view.el.select('.fc-event-container',true).first();
57590         
57591         var cls;
57592         this.eventStore.each(function(ev){
57593             
57594             this.renderEvent(ev);
57595              
57596              
57597         }, this);
57598         this.view.layout();
57599         
57600     },
57601     
57602     onEventEnter: function (e, el,event,d) {
57603         this.fireEvent('evententer', this, el, event);
57604     },
57605     
57606     onEventLeave: function (e, el,event,d) {
57607         this.fireEvent('eventleave', this, el, event);
57608     },
57609     
57610     onEventClick: function (e, el,event,d) {
57611         this.fireEvent('eventclick', this, el, event);
57612     },
57613     
57614     onMonthChange: function () {
57615         this.store.load();
57616     },
57617     
57618     onLoad: function () {
57619         
57620         //Roo.log('calendar onload');
57621 //         
57622         if(this.eventStore.getCount() > 0){
57623             
57624            
57625             
57626             this.eventStore.each(function(d){
57627                 
57628                 
57629                 // FIXME..
57630                 var add =   d.data;
57631                 if (typeof(add.end_dt) == 'undefined')  {
57632                     Roo.log("Missing End time in calendar data: ");
57633                     Roo.log(d);
57634                     return;
57635                 }
57636                 if (typeof(add.start_dt) == 'undefined')  {
57637                     Roo.log("Missing Start time in calendar data: ");
57638                     Roo.log(d);
57639                     return;
57640                 }
57641                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57642                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57643                 add.id = add.id || d.id;
57644                 add.title = add.title || '??';
57645                 
57646                 this.addItem(d);
57647                 
57648              
57649             },this);
57650         }
57651         
57652         this.renderEvents();
57653     }
57654     
57655
57656 });
57657 /*
57658  grid : {
57659                 xtype: 'Grid',
57660                 xns: Roo.grid,
57661                 listeners : {
57662                     render : function ()
57663                     {
57664                         _this.grid = this;
57665                         
57666                         if (!this.view.el.hasClass('course-timesheet')) {
57667                             this.view.el.addClass('course-timesheet');
57668                         }
57669                         if (this.tsStyle) {
57670                             this.ds.load({});
57671                             return; 
57672                         }
57673                         Roo.log('width');
57674                         Roo.log(_this.grid.view.el.getWidth());
57675                         
57676                         
57677                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57678                             '.course-timesheet .x-grid-row' : {
57679                                 height: '80px'
57680                             },
57681                             '.x-grid-row td' : {
57682                                 'vertical-align' : 0
57683                             },
57684                             '.course-edit-link' : {
57685                                 'color' : 'blue',
57686                                 'text-overflow' : 'ellipsis',
57687                                 'overflow' : 'hidden',
57688                                 'white-space' : 'nowrap',
57689                                 'cursor' : 'pointer'
57690                             },
57691                             '.sub-link' : {
57692                                 'color' : 'green'
57693                             },
57694                             '.de-act-sup-link' : {
57695                                 'color' : 'purple',
57696                                 'text-decoration' : 'line-through'
57697                             },
57698                             '.de-act-link' : {
57699                                 'color' : 'red',
57700                                 'text-decoration' : 'line-through'
57701                             },
57702                             '.course-timesheet .course-highlight' : {
57703                                 'border-top-style': 'dashed !important',
57704                                 'border-bottom-bottom': 'dashed !important'
57705                             },
57706                             '.course-timesheet .course-item' : {
57707                                 'font-family'   : 'tahoma, arial, helvetica',
57708                                 'font-size'     : '11px',
57709                                 'overflow'      : 'hidden',
57710                                 'padding-left'  : '10px',
57711                                 'padding-right' : '10px',
57712                                 'padding-top' : '10px' 
57713                             }
57714                             
57715                         }, Roo.id());
57716                                 this.ds.load({});
57717                     }
57718                 },
57719                 autoWidth : true,
57720                 monitorWindowResize : false,
57721                 cellrenderer : function(v,x,r)
57722                 {
57723                     return v;
57724                 },
57725                 sm : {
57726                     xtype: 'CellSelectionModel',
57727                     xns: Roo.grid
57728                 },
57729                 dataSource : {
57730                     xtype: 'Store',
57731                     xns: Roo.data,
57732                     listeners : {
57733                         beforeload : function (_self, options)
57734                         {
57735                             options.params = options.params || {};
57736                             options.params._month = _this.monthField.getValue();
57737                             options.params.limit = 9999;
57738                             options.params['sort'] = 'when_dt';    
57739                             options.params['dir'] = 'ASC';    
57740                             this.proxy.loadResponse = this.loadResponse;
57741                             Roo.log("load?");
57742                             //this.addColumns();
57743                         },
57744                         load : function (_self, records, options)
57745                         {
57746                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57747                                 // if you click on the translation.. you can edit it...
57748                                 var el = Roo.get(this);
57749                                 var id = el.dom.getAttribute('data-id');
57750                                 var d = el.dom.getAttribute('data-date');
57751                                 var t = el.dom.getAttribute('data-time');
57752                                 //var id = this.child('span').dom.textContent;
57753                                 
57754                                 //Roo.log(this);
57755                                 Pman.Dialog.CourseCalendar.show({
57756                                     id : id,
57757                                     when_d : d,
57758                                     when_t : t,
57759                                     productitem_active : id ? 1 : 0
57760                                 }, function() {
57761                                     _this.grid.ds.load({});
57762                                 });
57763                            
57764                            });
57765                            
57766                            _this.panel.fireEvent('resize', [ '', '' ]);
57767                         }
57768                     },
57769                     loadResponse : function(o, success, response){
57770                             // this is overridden on before load..
57771                             
57772                             Roo.log("our code?");       
57773                             //Roo.log(success);
57774                             //Roo.log(response)
57775                             delete this.activeRequest;
57776                             if(!success){
57777                                 this.fireEvent("loadexception", this, o, response);
57778                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57779                                 return;
57780                             }
57781                             var result;
57782                             try {
57783                                 result = o.reader.read(response);
57784                             }catch(e){
57785                                 Roo.log("load exception?");
57786                                 this.fireEvent("loadexception", this, o, response, e);
57787                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57788                                 return;
57789                             }
57790                             Roo.log("ready...");        
57791                             // loop through result.records;
57792                             // and set this.tdate[date] = [] << array of records..
57793                             _this.tdata  = {};
57794                             Roo.each(result.records, function(r){
57795                                 //Roo.log(r.data);
57796                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57797                                     _this.tdata[r.data.when_dt.format('j')] = [];
57798                                 }
57799                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57800                             });
57801                             
57802                             //Roo.log(_this.tdata);
57803                             
57804                             result.records = [];
57805                             result.totalRecords = 6;
57806                     
57807                             // let's generate some duumy records for the rows.
57808                             //var st = _this.dateField.getValue();
57809                             
57810                             // work out monday..
57811                             //st = st.add(Date.DAY, -1 * st.format('w'));
57812                             
57813                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57814                             
57815                             var firstOfMonth = date.getFirstDayOfMonth();
57816                             var days = date.getDaysInMonth();
57817                             var d = 1;
57818                             var firstAdded = false;
57819                             for (var i = 0; i < result.totalRecords ; i++) {
57820                                 //var d= st.add(Date.DAY, i);
57821                                 var row = {};
57822                                 var added = 0;
57823                                 for(var w = 0 ; w < 7 ; w++){
57824                                     if(!firstAdded && firstOfMonth != w){
57825                                         continue;
57826                                     }
57827                                     if(d > days){
57828                                         continue;
57829                                     }
57830                                     firstAdded = true;
57831                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57832                                     row['weekday'+w] = String.format(
57833                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57834                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57835                                                     d,
57836                                                     date.format('Y-m-')+dd
57837                                                 );
57838                                     added++;
57839                                     if(typeof(_this.tdata[d]) != 'undefined'){
57840                                         Roo.each(_this.tdata[d], function(r){
57841                                             var is_sub = '';
57842                                             var deactive = '';
57843                                             var id = r.id;
57844                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57845                                             if(r.parent_id*1>0){
57846                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57847                                                 id = r.parent_id;
57848                                             }
57849                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57850                                                 deactive = 'de-act-link';
57851                                             }
57852                                             
57853                                             row['weekday'+w] += String.format(
57854                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57855                                                     id, //0
57856                                                     r.product_id_name, //1
57857                                                     r.when_dt.format('h:ia'), //2
57858                                                     is_sub, //3
57859                                                     deactive, //4
57860                                                     desc // 5
57861                                             );
57862                                         });
57863                                     }
57864                                     d++;
57865                                 }
57866                                 
57867                                 // only do this if something added..
57868                                 if(added > 0){ 
57869                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57870                                 }
57871                                 
57872                                 
57873                                 // push it twice. (second one with an hour..
57874                                 
57875                             }
57876                             //Roo.log(result);
57877                             this.fireEvent("load", this, o, o.request.arg);
57878                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57879                         },
57880                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57881                     proxy : {
57882                         xtype: 'HttpProxy',
57883                         xns: Roo.data,
57884                         method : 'GET',
57885                         url : baseURL + '/Roo/Shop_course.php'
57886                     },
57887                     reader : {
57888                         xtype: 'JsonReader',
57889                         xns: Roo.data,
57890                         id : 'id',
57891                         fields : [
57892                             {
57893                                 'name': 'id',
57894                                 'type': 'int'
57895                             },
57896                             {
57897                                 'name': 'when_dt',
57898                                 'type': 'string'
57899                             },
57900                             {
57901                                 'name': 'end_dt',
57902                                 'type': 'string'
57903                             },
57904                             {
57905                                 'name': 'parent_id',
57906                                 'type': 'int'
57907                             },
57908                             {
57909                                 'name': 'product_id',
57910                                 'type': 'int'
57911                             },
57912                             {
57913                                 'name': 'productitem_id',
57914                                 'type': 'int'
57915                             },
57916                             {
57917                                 'name': 'guid',
57918                                 'type': 'int'
57919                             }
57920                         ]
57921                     }
57922                 },
57923                 toolbar : {
57924                     xtype: 'Toolbar',
57925                     xns: Roo,
57926                     items : [
57927                         {
57928                             xtype: 'Button',
57929                             xns: Roo.Toolbar,
57930                             listeners : {
57931                                 click : function (_self, e)
57932                                 {
57933                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57934                                     sd.setMonth(sd.getMonth()-1);
57935                                     _this.monthField.setValue(sd.format('Y-m-d'));
57936                                     _this.grid.ds.load({});
57937                                 }
57938                             },
57939                             text : "Back"
57940                         },
57941                         {
57942                             xtype: 'Separator',
57943                             xns: Roo.Toolbar
57944                         },
57945                         {
57946                             xtype: 'MonthField',
57947                             xns: Roo.form,
57948                             listeners : {
57949                                 render : function (_self)
57950                                 {
57951                                     _this.monthField = _self;
57952                                    // _this.monthField.set  today
57953                                 },
57954                                 select : function (combo, date)
57955                                 {
57956                                     _this.grid.ds.load({});
57957                                 }
57958                             },
57959                             value : (function() { return new Date(); })()
57960                         },
57961                         {
57962                             xtype: 'Separator',
57963                             xns: Roo.Toolbar
57964                         },
57965                         {
57966                             xtype: 'TextItem',
57967                             xns: Roo.Toolbar,
57968                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57969                         },
57970                         {
57971                             xtype: 'Fill',
57972                             xns: Roo.Toolbar
57973                         },
57974                         {
57975                             xtype: 'Button',
57976                             xns: Roo.Toolbar,
57977                             listeners : {
57978                                 click : function (_self, e)
57979                                 {
57980                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57981                                     sd.setMonth(sd.getMonth()+1);
57982                                     _this.monthField.setValue(sd.format('Y-m-d'));
57983                                     _this.grid.ds.load({});
57984                                 }
57985                             },
57986                             text : "Next"
57987                         }
57988                     ]
57989                 },
57990                  
57991             }
57992         };
57993         
57994         *//*
57995  * Based on:
57996  * Ext JS Library 1.1.1
57997  * Copyright(c) 2006-2007, Ext JS, LLC.
57998  *
57999  * Originally Released Under LGPL - original licence link has changed is not relivant.
58000  *
58001  * Fork - LGPL
58002  * <script type="text/javascript">
58003  */
58004  
58005 /**
58006  * @class Roo.LoadMask
58007  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58008  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58009  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58010  * element's UpdateManager load indicator and will be destroyed after the initial load.
58011  * @constructor
58012  * Create a new LoadMask
58013  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58014  * @param {Object} config The config object
58015  */
58016 Roo.LoadMask = function(el, config){
58017     this.el = Roo.get(el);
58018     Roo.apply(this, config);
58019     if(this.store){
58020         this.store.on('beforeload', this.onBeforeLoad, this);
58021         this.store.on('load', this.onLoad, this);
58022         this.store.on('loadexception', this.onLoadException, this);
58023         this.removeMask = false;
58024     }else{
58025         var um = this.el.getUpdateManager();
58026         um.showLoadIndicator = false; // disable the default indicator
58027         um.on('beforeupdate', this.onBeforeLoad, this);
58028         um.on('update', this.onLoad, this);
58029         um.on('failure', this.onLoad, this);
58030         this.removeMask = true;
58031     }
58032 };
58033
58034 Roo.LoadMask.prototype = {
58035     /**
58036      * @cfg {Boolean} removeMask
58037      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58038      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58039      */
58040     /**
58041      * @cfg {String} msg
58042      * The text to display in a centered loading message box (defaults to 'Loading...')
58043      */
58044     msg : 'Loading...',
58045     /**
58046      * @cfg {String} msgCls
58047      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58048      */
58049     msgCls : 'x-mask-loading',
58050
58051     /**
58052      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58053      * @type Boolean
58054      */
58055     disabled: false,
58056
58057     /**
58058      * Disables the mask to prevent it from being displayed
58059      */
58060     disable : function(){
58061        this.disabled = true;
58062     },
58063
58064     /**
58065      * Enables the mask so that it can be displayed
58066      */
58067     enable : function(){
58068         this.disabled = false;
58069     },
58070     
58071     onLoadException : function()
58072     {
58073         Roo.log(arguments);
58074         
58075         if (typeof(arguments[3]) != 'undefined') {
58076             Roo.MessageBox.alert("Error loading",arguments[3]);
58077         } 
58078         /*
58079         try {
58080             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58081                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58082             }   
58083         } catch(e) {
58084             
58085         }
58086         */
58087     
58088         
58089         
58090         this.el.unmask(this.removeMask);
58091     },
58092     // private
58093     onLoad : function()
58094     {
58095         this.el.unmask(this.removeMask);
58096     },
58097
58098     // private
58099     onBeforeLoad : function(){
58100         if(!this.disabled){
58101             this.el.mask(this.msg, this.msgCls);
58102         }
58103     },
58104
58105     // private
58106     destroy : function(){
58107         if(this.store){
58108             this.store.un('beforeload', this.onBeforeLoad, this);
58109             this.store.un('load', this.onLoad, this);
58110             this.store.un('loadexception', this.onLoadException, this);
58111         }else{
58112             var um = this.el.getUpdateManager();
58113             um.un('beforeupdate', this.onBeforeLoad, this);
58114             um.un('update', this.onLoad, this);
58115             um.un('failure', this.onLoad, this);
58116         }
58117     }
58118 };/*
58119  * Based on:
58120  * Ext JS Library 1.1.1
58121  * Copyright(c) 2006-2007, Ext JS, LLC.
58122  *
58123  * Originally Released Under LGPL - original licence link has changed is not relivant.
58124  *
58125  * Fork - LGPL
58126  * <script type="text/javascript">
58127  */
58128
58129
58130 /**
58131  * @class Roo.XTemplate
58132  * @extends Roo.Template
58133  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58134 <pre><code>
58135 var t = new Roo.XTemplate(
58136         '&lt;select name="{name}"&gt;',
58137                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58138         '&lt;/select&gt;'
58139 );
58140  
58141 // then append, applying the master template values
58142  </code></pre>
58143  *
58144  * Supported features:
58145  *
58146  *  Tags:
58147
58148 <pre><code>
58149       {a_variable} - output encoded.
58150       {a_variable.format:("Y-m-d")} - call a method on the variable
58151       {a_variable:raw} - unencoded output
58152       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58153       {a_variable:this.method_on_template(...)} - call a method on the template object.
58154  
58155 </code></pre>
58156  *  The tpl tag:
58157 <pre><code>
58158         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58159         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58160         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58161         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58162   
58163         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58164         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58165 </code></pre>
58166  *      
58167  */
58168 Roo.XTemplate = function()
58169 {
58170     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58171     if (this.html) {
58172         this.compile();
58173     }
58174 };
58175
58176
58177 Roo.extend(Roo.XTemplate, Roo.Template, {
58178
58179     /**
58180      * The various sub templates
58181      */
58182     tpls : false,
58183     /**
58184      *
58185      * basic tag replacing syntax
58186      * WORD:WORD()
58187      *
58188      * // you can fake an object call by doing this
58189      *  x.t:(test,tesT) 
58190      * 
58191      */
58192     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58193
58194     /**
58195      * compile the template
58196      *
58197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58198      *
58199      */
58200     compile: function()
58201     {
58202         var s = this.html;
58203      
58204         s = ['<tpl>', s, '</tpl>'].join('');
58205     
58206         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58207             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58208             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58209             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58210             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58211             m,
58212             id     = 0,
58213             tpls   = [];
58214     
58215         while(true == !!(m = s.match(re))){
58216             var forMatch   = m[0].match(nameRe),
58217                 ifMatch   = m[0].match(ifRe),
58218                 execMatch   = m[0].match(execRe),
58219                 namedMatch   = m[0].match(namedRe),
58220                 
58221                 exp  = null, 
58222                 fn   = null,
58223                 exec = null,
58224                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58225                 
58226             if (ifMatch) {
58227                 // if - puts fn into test..
58228                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58229                 if(exp){
58230                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58231                 }
58232             }
58233             
58234             if (execMatch) {
58235                 // exec - calls a function... returns empty if true is  returned.
58236                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58237                 if(exp){
58238                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58239                 }
58240             }
58241             
58242             
58243             if (name) {
58244                 // for = 
58245                 switch(name){
58246                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58247                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58248                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58249                 }
58250             }
58251             var uid = namedMatch ? namedMatch[1] : id;
58252             
58253             
58254             tpls.push({
58255                 id:     namedMatch ? namedMatch[1] : id,
58256                 target: name,
58257                 exec:   exec,
58258                 test:   fn,
58259                 body:   m[1] || ''
58260             });
58261             if (namedMatch) {
58262                 s = s.replace(m[0], '');
58263             } else { 
58264                 s = s.replace(m[0], '{xtpl'+ id + '}');
58265             }
58266             ++id;
58267         }
58268         this.tpls = [];
58269         for(var i = tpls.length-1; i >= 0; --i){
58270             this.compileTpl(tpls[i]);
58271             this.tpls[tpls[i].id] = tpls[i];
58272         }
58273         this.master = tpls[tpls.length-1];
58274         return this;
58275     },
58276     /**
58277      * same as applyTemplate, except it's done to one of the subTemplates
58278      * when using named templates, you can do:
58279      *
58280      * var str = pl.applySubTemplate('your-name', values);
58281      *
58282      * 
58283      * @param {Number} id of the template
58284      * @param {Object} values to apply to template
58285      * @param {Object} parent (normaly the instance of this object)
58286      */
58287     applySubTemplate : function(id, values, parent)
58288     {
58289         
58290         
58291         var t = this.tpls[id];
58292         
58293         
58294         try { 
58295             if(t.test && !t.test.call(this, values, parent)){
58296                 return '';
58297             }
58298         } catch(e) {
58299             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58300             Roo.log(e.toString());
58301             Roo.log(t.test);
58302             return ''
58303         }
58304         try { 
58305             
58306             if(t.exec && t.exec.call(this, values, parent)){
58307                 return '';
58308             }
58309         } catch(e) {
58310             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58311             Roo.log(e.toString());
58312             Roo.log(t.exec);
58313             return ''
58314         }
58315         try {
58316             var vs = t.target ? t.target.call(this, values, parent) : values;
58317             parent = t.target ? values : parent;
58318             if(t.target && vs instanceof Array){
58319                 var buf = [];
58320                 for(var i = 0, len = vs.length; i < len; i++){
58321                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58322                 }
58323                 return buf.join('');
58324             }
58325             return t.compiled.call(this, vs, parent);
58326         } catch (e) {
58327             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58328             Roo.log(e.toString());
58329             Roo.log(t.compiled);
58330             return '';
58331         }
58332     },
58333
58334     compileTpl : function(tpl)
58335     {
58336         var fm = Roo.util.Format;
58337         var useF = this.disableFormats !== true;
58338         var sep = Roo.isGecko ? "+" : ",";
58339         var undef = function(str) {
58340             Roo.log("Property not found :"  + str);
58341             return '';
58342         };
58343         
58344         var fn = function(m, name, format, args)
58345         {
58346             //Roo.log(arguments);
58347             args = args ? args.replace(/\\'/g,"'") : args;
58348             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58349             if (typeof(format) == 'undefined') {
58350                 format= 'htmlEncode';
58351             }
58352             if (format == 'raw' ) {
58353                 format = false;
58354             }
58355             
58356             if(name.substr(0, 4) == 'xtpl'){
58357                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58358             }
58359             
58360             // build an array of options to determine if value is undefined..
58361             
58362             // basically get 'xxxx.yyyy' then do
58363             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58364             //    (function () { Roo.log("Property not found"); return ''; })() :
58365             //    ......
58366             
58367             var udef_ar = [];
58368             var lookfor = '';
58369             Roo.each(name.split('.'), function(st) {
58370                 lookfor += (lookfor.length ? '.': '') + st;
58371                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58372             });
58373             
58374             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58375             
58376             
58377             if(format && useF){
58378                 
58379                 args = args ? ',' + args : "";
58380                  
58381                 if(format.substr(0, 5) != "this."){
58382                     format = "fm." + format + '(';
58383                 }else{
58384                     format = 'this.call("'+ format.substr(5) + '", ';
58385                     args = ", values";
58386                 }
58387                 
58388                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58389             }
58390              
58391             if (args.length) {
58392                 // called with xxyx.yuu:(test,test)
58393                 // change to ()
58394                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58395             }
58396             // raw.. - :raw modifier..
58397             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58398             
58399         };
58400         var body;
58401         // branched to use + in gecko and [].join() in others
58402         if(Roo.isGecko){
58403             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58404                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58405                     "';};};";
58406         }else{
58407             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58408             body.push(tpl.body.replace(/(\r\n|\n)/g,
58409                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58410             body.push("'].join('');};};");
58411             body = body.join('');
58412         }
58413         
58414         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58415        
58416         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58417         eval(body);
58418         
58419         return this;
58420     },
58421
58422     applyTemplate : function(values){
58423         return this.master.compiled.call(this, values, {});
58424         //var s = this.subs;
58425     },
58426
58427     apply : function(){
58428         return this.applyTemplate.apply(this, arguments);
58429     }
58430
58431  });
58432
58433 Roo.XTemplate.from = function(el){
58434     el = Roo.getDom(el);
58435     return new Roo.XTemplate(el.value || el.innerHTML);
58436 };