roojs-ui.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  * @static
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         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * 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.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * 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]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 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
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y); v.setFullYear(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /**
4901  * Originally based of this code... - refactored for Roo...
4902  * https://github.com/aaalsaleh/undo-manager
4903  
4904  * undo-manager.js
4905  * @author  Abdulrahman Alsaleh 
4906  * @copyright 2015 Abdulrahman Alsaleh 
4907  * @license  MIT License (c) 
4908  *
4909  * Hackily modifyed by alan@roojs.com
4910  *
4911  *
4912  *  
4913  *
4914  *  TOTALLY UNTESTED...
4915  *
4916  *  Documentation to be done....
4917  */
4918  
4919
4920 /**
4921 * @class Roo.lib.UndoManager
4922 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4923 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4924
4925  * Usage:
4926  * <pre><code>
4927
4928
4929 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4930  
4931 </code></pre>
4932
4933 * For more information see this blog post with examples:
4934 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4935      - Create Elements using DOM, HTML fragments and Templates</a>. 
4936 * @constructor
4937 * @param {Number} limit how far back to go ... use 1000?
4938 * @param {Object} scope usually use document..
4939 */
4940
4941 Roo.lib.UndoManager = function (limit, undoScopeHost)
4942 {
4943     this.stack = [];
4944     this.limit = limit;
4945     this.scope = undoScopeHost;
4946     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4947     if (this.fireEvent) {
4948         this.bindEvents();
4949     }
4950     
4951 };
4952         
4953 Roo.lib.UndoManager.prototype = {
4954     
4955     limit : false,
4956     stack : false,
4957     scope :  false,
4958     fireEvent : false,
4959     position : 0,
4960     length : 0,
4961     
4962     
4963      /**
4964      * To push and execute a transaction, the method undoManager.transact
4965      * must be called by passing a transaction object as the first argument, and a merge
4966      * flag as the second argument. A transaction object has the following properties:
4967      *
4968      * Usage:
4969 <pre><code>
4970 undoManager.transact({
4971     label: 'Typing',
4972     execute: function() { ... },
4973     undo: function() { ... },
4974     // redo same as execute
4975     redo: function() { this.execute(); }
4976 }, false);
4977
4978 // merge transaction
4979 undoManager.transact({
4980     label: 'Typing',
4981     execute: function() { ... },  // this will be run...
4982     undo: function() { ... }, // what to do when undo is run.
4983     // redo same as execute
4984     redo: function() { this.execute(); }
4985 }, true); 
4986 </code></pre> 
4987      *
4988      * 
4989      * @param {Object} transaction The transaction to add to the stack.
4990      * @return {String} The HTML fragment
4991      */
4992     
4993     
4994     transact : function (transaction, merge)
4995     {
4996         if (arguments.length < 2) {
4997             throw new TypeError('Not enough arguments to UndoManager.transact.');
4998         }
4999
5000         transaction.execute();
5001
5002         this.stack.splice(0, this.position);
5003         if (merge && this.length) {
5004             this.stack[0].push(transaction);
5005         } else {
5006             this.stack.unshift([transaction]);
5007         }
5008     
5009         this.position = 0;
5010
5011         if (this.limit && this.stack.length > this.limit) {
5012             this.length = this.stack.length = this.limit;
5013         } else {
5014             this.length = this.stack.length;
5015         }
5016
5017         if (this.fireEvent) {
5018             this.scope.dispatchEvent(
5019                 new CustomEvent('DOMTransaction', {
5020                     detail: {
5021                         transactions: this.stack[0].slice()
5022                     },
5023                     bubbles: true,
5024                     cancelable: false
5025                 })
5026             );
5027         }
5028     },
5029
5030     undo : function ()
5031     {
5032         if (this.position < this.length) {
5033             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5034                 this.stack[this.position][i].undo();
5035             }
5036             this.position++;
5037
5038             if (this.fireEvent) {
5039                 this.scope.dispatchEvent(
5040                     new CustomEvent('undo', {
5041                         detail: {
5042                             transactions: this.stack[this.position - 1].slice()
5043                         },
5044                         bubbles: true,
5045                         cancelable: false
5046                     })
5047                 );
5048             }
5049         }
5050     },
5051
5052     redo : function ()
5053     {
5054         if (this.position > 0) {
5055             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5056                 this.stack[this.position - 1][i].redo();
5057             }
5058             this.position--;
5059
5060             if (this.fireEvent) {
5061                 this.scope.dispatchEvent(
5062                     new CustomEvent('redo', {
5063                         detail: {
5064                             transactions: this.stack[this.position].slice()
5065                         },
5066                         bubbles: true,
5067                         cancelable: false
5068                     })
5069                 );
5070             }
5071         }
5072     },
5073
5074     item : function (index)
5075     {
5076         if (index >= 0 && index < this.length) {
5077             return this.stack[index].slice();
5078         }
5079         return null;
5080     },
5081
5082     clearUndo : function () {
5083         this.stack.length = this.length = this.position;
5084     },
5085
5086     clearRedo : function () {
5087         this.stack.splice(0, this.position);
5088         this.position = 0;
5089         this.length = this.stack.length;
5090     },
5091     /**
5092      * Reset the undo - probaly done on load to clear all history.
5093      */
5094     reset : function()
5095     {
5096         this.stack = [];
5097         this.position = 0;
5098         this.length = 0;
5099         this.current_html = this.scope.innerHTML;
5100         if (this.timer !== false) {
5101             clearTimeout(this.timer);
5102         }
5103         this.timer = false;
5104         this.merge = false;
5105         
5106     },
5107     current_html : '',
5108     timer : false,
5109     merge : false,
5110     
5111     
5112     // this will handle the undo/redo on the element.?
5113     bindEvents : function()
5114     {
5115         var el  = this.scope;
5116         el.undoManager = this;
5117         
5118         
5119         this.scope.addEventListener('keydown', function(e) {
5120             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5121                 if (e.shiftKey) {
5122                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5123                 } else {
5124                     el.undoManager.undo(); // Ctrl/Command + Z
5125                 }
5126         
5127                 e.preventDefault();
5128             }
5129         });
5130         
5131         
5132         var t = this;
5133         
5134         el.addEventListener('input', function(e) {
5135             if(el.innerHTML == t.current_html) {
5136                 return;
5137             }
5138             // only record events every second.
5139             if (t.timer !== false) {
5140                clearTimeout(t.timer);
5141                t.timer = false;
5142             }
5143             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5144             
5145             t.addEvent(t.merge);
5146             t.merge = true; // ignore changes happening every second..
5147         });
5148         },
5149     /**
5150      * Manually add an event.
5151      * Normall called without arguements - and it will just get added to the stack.
5152      * 
5153      */
5154     
5155     addEvent : function(merge)
5156     {
5157         // not sure if this should clear the timer 
5158         merge = typeof(merge) == 'undefined' ? false : merge; 
5159         
5160         el.undoManager.transact({
5161             oldHTML: this.current_html,
5162             newHTML: this.scope.innerHTML,
5163             // nothing to execute (content already changed when input is fired)
5164             execute: function() { },
5165             undo: function() {
5166                 this.scope.innerHTML = this.current_html = this.oldHTML;
5167             },
5168             redo: function() {
5169                 this.scope.innerHTML = this.current_html = this.newHTML;
5170             }
5171         }, merge);
5172         
5173         this.merge = merge;
5174         
5175         this.current_html = el.innerHTML;
5176     }
5177     
5178     
5179      
5180     
5181     
5182     
5183 };
5184 /*
5185  * Based on:
5186  * Ext JS Library 1.1.1
5187  * Copyright(c) 2006-2007, Ext JS, LLC.
5188  *
5189  * Originally Released Under LGPL - original licence link has changed is not relivant.
5190  *
5191  * Fork - LGPL
5192  * <script type="text/javascript">
5193  */
5194
5195
5196 // nasty IE9 hack - what a pile of crap that is..
5197
5198  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5199     Range.prototype.createContextualFragment = function (html) {
5200         var doc = window.document;
5201         var container = doc.createElement("div");
5202         container.innerHTML = html;
5203         var frag = doc.createDocumentFragment(), n;
5204         while ((n = container.firstChild)) {
5205             frag.appendChild(n);
5206         }
5207         return frag;
5208     };
5209 }
5210
5211 /**
5212  * @class Roo.DomHelper
5213  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5214  * 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>.
5215  * @static
5216  */
5217 Roo.DomHelper = function(){
5218     var tempTableEl = null;
5219     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5220     var tableRe = /^table|tbody|tr|td$/i;
5221     var xmlns = {};
5222     // build as innerHTML where available
5223     /** @ignore */
5224     var createHtml = function(o){
5225         if(typeof o == 'string'){
5226             return o;
5227         }
5228         var b = "";
5229         if(!o.tag){
5230             o.tag = "div";
5231         }
5232         b += "<" + o.tag;
5233         for(var attr in o){
5234             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5235             if(attr == "style"){
5236                 var s = o["style"];
5237                 if(typeof s == "function"){
5238                     s = s.call();
5239                 }
5240                 if(typeof s == "string"){
5241                     b += ' style="' + s + '"';
5242                 }else if(typeof s == "object"){
5243                     b += ' style="';
5244                     for(var key in s){
5245                         if(typeof s[key] != "function"){
5246                             b += key + ":" + s[key] + ";";
5247                         }
5248                     }
5249                     b += '"';
5250                 }
5251             }else{
5252                 if(attr == "cls"){
5253                     b += ' class="' + o["cls"] + '"';
5254                 }else if(attr == "htmlFor"){
5255                     b += ' for="' + o["htmlFor"] + '"';
5256                 }else{
5257                     b += " " + attr + '="' + o[attr] + '"';
5258                 }
5259             }
5260         }
5261         if(emptyTags.test(o.tag)){
5262             b += "/>";
5263         }else{
5264             b += ">";
5265             var cn = o.children || o.cn;
5266             if(cn){
5267                 //http://bugs.kde.org/show_bug.cgi?id=71506
5268                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5269                     for(var i = 0, len = cn.length; i < len; i++) {
5270                         b += createHtml(cn[i], b);
5271                     }
5272                 }else{
5273                     b += createHtml(cn, b);
5274                 }
5275             }
5276             if(o.html){
5277                 b += o.html;
5278             }
5279             b += "</" + o.tag + ">";
5280         }
5281         return b;
5282     };
5283
5284     // build as dom
5285     /** @ignore */
5286     var createDom = function(o, parentNode){
5287          
5288         // defininition craeted..
5289         var ns = false;
5290         if (o.ns && o.ns != 'html') {
5291                
5292             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5293                 xmlns[o.ns] = o.xmlns;
5294                 ns = o.xmlns;
5295             }
5296             if (typeof(xmlns[o.ns]) == 'undefined') {
5297                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5298             }
5299             ns = xmlns[o.ns];
5300         }
5301         
5302         
5303         if (typeof(o) == 'string') {
5304             return parentNode.appendChild(document.createTextNode(o));
5305         }
5306         o.tag = o.tag || div;
5307         if (o.ns && Roo.isIE) {
5308             ns = false;
5309             o.tag = o.ns + ':' + o.tag;
5310             
5311         }
5312         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5313         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5314         for(var attr in o){
5315             
5316             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5317                     attr == "style" || typeof o[attr] == "function") { continue; }
5318                     
5319             if(attr=="cls" && Roo.isIE){
5320                 el.className = o["cls"];
5321             }else{
5322                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5323                 else { 
5324                     el[attr] = o[attr];
5325                 }
5326             }
5327         }
5328         Roo.DomHelper.applyStyles(el, o.style);
5329         var cn = o.children || o.cn;
5330         if(cn){
5331             //http://bugs.kde.org/show_bug.cgi?id=71506
5332              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5333                 for(var i = 0, len = cn.length; i < len; i++) {
5334                     createDom(cn[i], el);
5335                 }
5336             }else{
5337                 createDom(cn, el);
5338             }
5339         }
5340         if(o.html){
5341             el.innerHTML = o.html;
5342         }
5343         if(parentNode){
5344            parentNode.appendChild(el);
5345         }
5346         return el;
5347     };
5348
5349     var ieTable = function(depth, s, h, e){
5350         tempTableEl.innerHTML = [s, h, e].join('');
5351         var i = -1, el = tempTableEl;
5352         while(++i < depth && el.firstChild){
5353             el = el.firstChild;
5354         }
5355         return el;
5356     };
5357
5358     // kill repeat to save bytes
5359     var ts = '<table>',
5360         te = '</table>',
5361         tbs = ts+'<tbody>',
5362         tbe = '</tbody>'+te,
5363         trs = tbs + '<tr>',
5364         tre = '</tr>'+tbe;
5365
5366     /**
5367      * @ignore
5368      * Nasty code for IE's broken table implementation
5369      */
5370     var insertIntoTable = function(tag, where, el, html){
5371         if(!tempTableEl){
5372             tempTableEl = document.createElement('div');
5373         }
5374         var node;
5375         var before = null;
5376         if(tag == 'td'){
5377             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5378                 return;
5379             }
5380             if(where == 'beforebegin'){
5381                 before = el;
5382                 el = el.parentNode;
5383             } else{
5384                 before = el.nextSibling;
5385                 el = el.parentNode;
5386             }
5387             node = ieTable(4, trs, html, tre);
5388         }
5389         else if(tag == 'tr'){
5390             if(where == 'beforebegin'){
5391                 before = el;
5392                 el = el.parentNode;
5393                 node = ieTable(3, tbs, html, tbe);
5394             } else if(where == 'afterend'){
5395                 before = el.nextSibling;
5396                 el = el.parentNode;
5397                 node = ieTable(3, tbs, html, tbe);
5398             } else{ // INTO a TR
5399                 if(where == 'afterbegin'){
5400                     before = el.firstChild;
5401                 }
5402                 node = ieTable(4, trs, html, tre);
5403             }
5404         } else if(tag == 'tbody'){
5405             if(where == 'beforebegin'){
5406                 before = el;
5407                 el = el.parentNode;
5408                 node = ieTable(2, ts, html, te);
5409             } else if(where == 'afterend'){
5410                 before = el.nextSibling;
5411                 el = el.parentNode;
5412                 node = ieTable(2, ts, html, te);
5413             } else{
5414                 if(where == 'afterbegin'){
5415                     before = el.firstChild;
5416                 }
5417                 node = ieTable(3, tbs, html, tbe);
5418             }
5419         } else{ // TABLE
5420             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5421                 return;
5422             }
5423             if(where == 'afterbegin'){
5424                 before = el.firstChild;
5425             }
5426             node = ieTable(2, ts, html, te);
5427         }
5428         el.insertBefore(node, before);
5429         return node;
5430     };
5431     
5432     // this is a bit like the react update code...
5433     // 
5434     
5435     var updateNode = function(from, to)
5436     {
5437         // should we handle non-standard elements?
5438         Roo.log(["UpdateNode" , from, to]);
5439         if (from.nodeType != to.nodeType) {
5440             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5441             from.parentNode.replaceChild(to, from);
5442         }
5443         
5444         if (from.nodeType == 3) {
5445             // assume it's text?!
5446             if (from.data == to.data) {
5447                 return;
5448             }
5449             from.data = to.data;
5450             return;
5451         }
5452         
5453         // assume 'to' doesnt have '1/3 nodetypes!
5454         if (from.nodeType !=1 || from.tagName != to.tagName) {
5455             Roo.log(["ReplaceChild" , from, to ]);
5456             from.parentNode.replaceChild(to, from);
5457             return;
5458         }
5459         // compare attributes
5460         var ar = Array.from(from.attributes);
5461         for(var i = 0; i< ar.length;i++) {
5462             if (to.hasAttribute(ar[i].name)) {
5463                 continue;
5464             }
5465             if (ar[i].name == 'id') { // always keep ids?
5466                 continue;
5467             }
5468             from.removeAttribute(ar[i].name);
5469         }
5470         ar = to.attributes;
5471         for(var i = 0; i< ar.length;i++) {
5472             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5473                 continue;
5474             }
5475             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5476         }
5477         // children
5478         var far = Array.from(from.childNodes);
5479         var tar = Array.from(to.childNodes);
5480         // if the lengths are different.. then it's probably a editable content change, rather than
5481         // a change of the block definition..
5482         
5483         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5484          /*if (from.innerHTML == to.innerHTML) {
5485             return;
5486         }
5487         if (far.length != tar.length) {
5488             from.innerHTML = to.innerHTML;
5489             return;
5490         }
5491         */
5492         
5493         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5494             if (i >= far.length) {
5495                 from.appendChild(tar[i]);
5496                 Roo.log(["add", tar[i]]);
5497                 
5498             } else if ( i  >= tar.length) {
5499                 from.removeChild(far[i]);
5500                 Roo.log(["remove", far[i]]);
5501             } else {
5502                 
5503                 updateNode(far[i], tar[i]);
5504             }    
5505         }
5506         
5507         
5508         
5509         
5510     };
5511     
5512     
5513
5514     return {
5515         /** True to force the use of DOM instead of html fragments @type Boolean */
5516         useDom : false,
5517     
5518         /**
5519          * Returns the markup for the passed Element(s) config
5520          * @param {Object} o The Dom object spec (and children)
5521          * @return {String}
5522          */
5523         markup : function(o){
5524             return createHtml(o);
5525         },
5526     
5527         /**
5528          * Applies a style specification to an element
5529          * @param {String/HTMLElement} el The element to apply styles to
5530          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5531          * a function which returns such a specification.
5532          */
5533         applyStyles : function(el, styles){
5534             if(styles){
5535                el = Roo.fly(el);
5536                if(typeof styles == "string"){
5537                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5538                    var matches;
5539                    while ((matches = re.exec(styles)) != null){
5540                        el.setStyle(matches[1], matches[2]);
5541                    }
5542                }else if (typeof styles == "object"){
5543                    for (var style in styles){
5544                       el.setStyle(style, styles[style]);
5545                    }
5546                }else if (typeof styles == "function"){
5547                     Roo.DomHelper.applyStyles(el, styles.call());
5548                }
5549             }
5550         },
5551     
5552         /**
5553          * Inserts an HTML fragment into the Dom
5554          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5555          * @param {HTMLElement} el The context element
5556          * @param {String} html The HTML fragmenet
5557          * @return {HTMLElement} The new node
5558          */
5559         insertHtml : function(where, el, html){
5560             where = where.toLowerCase();
5561             if(el.insertAdjacentHTML){
5562                 if(tableRe.test(el.tagName)){
5563                     var rs;
5564                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5565                         return rs;
5566                     }
5567                 }
5568                 switch(where){
5569                     case "beforebegin":
5570                         el.insertAdjacentHTML('BeforeBegin', html);
5571                         return el.previousSibling;
5572                     case "afterbegin":
5573                         el.insertAdjacentHTML('AfterBegin', html);
5574                         return el.firstChild;
5575                     case "beforeend":
5576                         el.insertAdjacentHTML('BeforeEnd', html);
5577                         return el.lastChild;
5578                     case "afterend":
5579                         el.insertAdjacentHTML('AfterEnd', html);
5580                         return el.nextSibling;
5581                 }
5582                 throw 'Illegal insertion point -> "' + where + '"';
5583             }
5584             var range = el.ownerDocument.createRange();
5585             var frag;
5586             switch(where){
5587                  case "beforebegin":
5588                     range.setStartBefore(el);
5589                     frag = range.createContextualFragment(html);
5590                     el.parentNode.insertBefore(frag, el);
5591                     return el.previousSibling;
5592                  case "afterbegin":
5593                     if(el.firstChild){
5594                         range.setStartBefore(el.firstChild);
5595                         frag = range.createContextualFragment(html);
5596                         el.insertBefore(frag, el.firstChild);
5597                         return el.firstChild;
5598                     }else{
5599                         el.innerHTML = html;
5600                         return el.firstChild;
5601                     }
5602                 case "beforeend":
5603                     if(el.lastChild){
5604                         range.setStartAfter(el.lastChild);
5605                         frag = range.createContextualFragment(html);
5606                         el.appendChild(frag);
5607                         return el.lastChild;
5608                     }else{
5609                         el.innerHTML = html;
5610                         return el.lastChild;
5611                     }
5612                 case "afterend":
5613                     range.setStartAfter(el);
5614                     frag = range.createContextualFragment(html);
5615                     el.parentNode.insertBefore(frag, el.nextSibling);
5616                     return el.nextSibling;
5617                 }
5618                 throw 'Illegal insertion point -> "' + where + '"';
5619         },
5620     
5621         /**
5622          * Creates new Dom element(s) and inserts them before el
5623          * @param {String/HTMLElement/Element} el The context element
5624          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5625          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5626          * @return {HTMLElement/Roo.Element} The new node
5627          */
5628         insertBefore : function(el, o, returnElement){
5629             return this.doInsert(el, o, returnElement, "beforeBegin");
5630         },
5631     
5632         /**
5633          * Creates new Dom element(s) and inserts them after el
5634          * @param {String/HTMLElement/Element} el The context element
5635          * @param {Object} o The Dom object spec (and children)
5636          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5637          * @return {HTMLElement/Roo.Element} The new node
5638          */
5639         insertAfter : function(el, o, returnElement){
5640             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5641         },
5642     
5643         /**
5644          * Creates new Dom element(s) and inserts them as the first child of el
5645          * @param {String/HTMLElement/Element} el The context element
5646          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5647          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5648          * @return {HTMLElement/Roo.Element} The new node
5649          */
5650         insertFirst : function(el, o, returnElement){
5651             return this.doInsert(el, o, returnElement, "afterBegin");
5652         },
5653     
5654         // private
5655         doInsert : function(el, o, returnElement, pos, sibling){
5656             el = Roo.getDom(el);
5657             var newNode;
5658             if(this.useDom || o.ns){
5659                 newNode = createDom(o, null);
5660                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5661             }else{
5662                 var html = createHtml(o);
5663                 newNode = this.insertHtml(pos, el, html);
5664             }
5665             return returnElement ? Roo.get(newNode, true) : newNode;
5666         },
5667     
5668         /**
5669          * Creates new Dom element(s) and appends them to el
5670          * @param {String/HTMLElement/Element} el The context element
5671          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5672          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5673          * @return {HTMLElement/Roo.Element} The new node
5674          */
5675         append : function(el, o, returnElement){
5676             el = Roo.getDom(el);
5677             var newNode;
5678             if(this.useDom || o.ns){
5679                 newNode = createDom(o, null);
5680                 el.appendChild(newNode);
5681             }else{
5682                 var html = createHtml(o);
5683                 newNode = this.insertHtml("beforeEnd", el, html);
5684             }
5685             return returnElement ? Roo.get(newNode, true) : newNode;
5686         },
5687     
5688         /**
5689          * Creates new Dom element(s) and overwrites the contents of el with them
5690          * @param {String/HTMLElement/Element} el The context element
5691          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5692          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5693          * @return {HTMLElement/Roo.Element} The new node
5694          */
5695         overwrite : function(el, o, returnElement)
5696         {
5697             el = Roo.getDom(el);
5698             if (o.ns) {
5699               
5700                 while (el.childNodes.length) {
5701                     el.removeChild(el.firstChild);
5702                 }
5703                 createDom(o, el);
5704             } else {
5705                 el.innerHTML = createHtml(o);   
5706             }
5707             
5708             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5709         },
5710     
5711         /**
5712          * Creates a new Roo.DomHelper.Template from the Dom object spec
5713          * @param {Object} o The Dom object spec (and children)
5714          * @return {Roo.DomHelper.Template} The new template
5715          */
5716         createTemplate : function(o){
5717             var html = createHtml(o);
5718             return new Roo.Template(html);
5719         },
5720          /**
5721          * Updates the first element with the spec from the o (replacing if necessary)
5722          * This iterates through the children, and updates attributes / children etc..
5723          * @param {String/HTMLElement/Element} el The context element
5724          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5725          */
5726         
5727         update : function(el, o)
5728         {
5729             updateNode(Roo.getDom(el), createDom(o));
5730             
5731         }
5732         
5733         
5734     };
5735 }();
5736 /*
5737  * Based on:
5738  * Ext JS Library 1.1.1
5739  * Copyright(c) 2006-2007, Ext JS, LLC.
5740  *
5741  * Originally Released Under LGPL - original licence link has changed is not relivant.
5742  *
5743  * Fork - LGPL
5744  * <script type="text/javascript">
5745  */
5746  
5747 /**
5748 * @class Roo.Template
5749 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5750 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5751 * Usage:
5752 <pre><code>
5753 var t = new Roo.Template({
5754     html :  '&lt;div name="{id}"&gt;' + 
5755         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5756         '&lt;/div&gt;',
5757     myformat: function (value, allValues) {
5758         return 'XX' + value;
5759     }
5760 });
5761 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5762 </code></pre>
5763 * For more information see this blog post with examples:
5764 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5765      - Create Elements using DOM, HTML fragments and Templates</a>. 
5766 * @constructor
5767 * @param {Object} cfg - Configuration object.
5768 */
5769 Roo.Template = function(cfg){
5770     // BC!
5771     if(cfg instanceof Array){
5772         cfg = cfg.join("");
5773     }else if(arguments.length > 1){
5774         cfg = Array.prototype.join.call(arguments, "");
5775     }
5776     
5777     
5778     if (typeof(cfg) == 'object') {
5779         Roo.apply(this,cfg)
5780     } else {
5781         // bc
5782         this.html = cfg;
5783     }
5784     if (this.url) {
5785         this.load();
5786     }
5787     
5788 };
5789 Roo.Template.prototype = {
5790     
5791     /**
5792      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5793      */
5794     onLoad : false,
5795     
5796     
5797     /**
5798      * @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..
5799      *                    it should be fixed so that template is observable...
5800      */
5801     url : false,
5802     /**
5803      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5804      */
5805     html : '',
5806     
5807     
5808     compiled : false,
5809     loaded : false,
5810     /**
5811      * Returns an HTML fragment of this template with the specified values applied.
5812      * @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'})
5813      * @return {String} The HTML fragment
5814      */
5815     
5816    
5817     
5818     applyTemplate : function(values){
5819         //Roo.log(["applyTemplate", values]);
5820         try {
5821            
5822             if(this.compiled){
5823                 return this.compiled(values);
5824             }
5825             var useF = this.disableFormats !== true;
5826             var fm = Roo.util.Format, tpl = this;
5827             var fn = function(m, name, format, args){
5828                 if(format && useF){
5829                     if(format.substr(0, 5) == "this."){
5830                         return tpl.call(format.substr(5), values[name], values);
5831                     }else{
5832                         if(args){
5833                             // quoted values are required for strings in compiled templates, 
5834                             // but for non compiled we need to strip them
5835                             // quoted reversed for jsmin
5836                             var re = /^\s*['"](.*)["']\s*$/;
5837                             args = args.split(',');
5838                             for(var i = 0, len = args.length; i < len; i++){
5839                                 args[i] = args[i].replace(re, "$1");
5840                             }
5841                             args = [values[name]].concat(args);
5842                         }else{
5843                             args = [values[name]];
5844                         }
5845                         return fm[format].apply(fm, args);
5846                     }
5847                 }else{
5848                     return values[name] !== undefined ? values[name] : "";
5849                 }
5850             };
5851             return this.html.replace(this.re, fn);
5852         } catch (e) {
5853             Roo.log(e);
5854             throw e;
5855         }
5856          
5857     },
5858     
5859     loading : false,
5860       
5861     load : function ()
5862     {
5863          
5864         if (this.loading) {
5865             return;
5866         }
5867         var _t = this;
5868         
5869         this.loading = true;
5870         this.compiled = false;
5871         
5872         var cx = new Roo.data.Connection();
5873         cx.request({
5874             url : this.url,
5875             method : 'GET',
5876             success : function (response) {
5877                 _t.loading = false;
5878                 _t.url = false;
5879                 
5880                 _t.set(response.responseText,true);
5881                 _t.loaded = true;
5882                 if (_t.onLoad) {
5883                     _t.onLoad();
5884                 }
5885              },
5886             failure : function(response) {
5887                 Roo.log("Template failed to load from " + _t.url);
5888                 _t.loading = false;
5889             }
5890         });
5891     },
5892
5893     /**
5894      * Sets the HTML used as the template and optionally compiles it.
5895      * @param {String} html
5896      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5897      * @return {Roo.Template} this
5898      */
5899     set : function(html, compile){
5900         this.html = html;
5901         this.compiled = false;
5902         if(compile){
5903             this.compile();
5904         }
5905         return this;
5906     },
5907     
5908     /**
5909      * True to disable format functions (defaults to false)
5910      * @type Boolean
5911      */
5912     disableFormats : false,
5913     
5914     /**
5915     * The regular expression used to match template variables 
5916     * @type RegExp
5917     * @property 
5918     */
5919     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5920     
5921     /**
5922      * Compiles the template into an internal function, eliminating the RegEx overhead.
5923      * @return {Roo.Template} this
5924      */
5925     compile : function(){
5926         var fm = Roo.util.Format;
5927         var useF = this.disableFormats !== true;
5928         var sep = Roo.isGecko ? "+" : ",";
5929         var fn = function(m, name, format, args){
5930             if(format && useF){
5931                 args = args ? ',' + args : "";
5932                 if(format.substr(0, 5) != "this."){
5933                     format = "fm." + format + '(';
5934                 }else{
5935                     format = 'this.call("'+ format.substr(5) + '", ';
5936                     args = ", values";
5937                 }
5938             }else{
5939                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5940             }
5941             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5942         };
5943         var body;
5944         // branched to use + in gecko and [].join() in others
5945         if(Roo.isGecko){
5946             body = "this.compiled = function(values){ return '" +
5947                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5948                     "';};";
5949         }else{
5950             body = ["this.compiled = function(values){ return ['"];
5951             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5952             body.push("'].join('');};");
5953             body = body.join('');
5954         }
5955         /**
5956          * eval:var:values
5957          * eval:var:fm
5958          */
5959         eval(body);
5960         return this;
5961     },
5962     
5963     // private function used to call members
5964     call : function(fnName, value, allValues){
5965         return this[fnName](value, allValues);
5966     },
5967     
5968     /**
5969      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5970      * @param {String/HTMLElement/Roo.Element} el The context element
5971      * @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'})
5972      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5973      * @return {HTMLElement/Roo.Element} The new node or Element
5974      */
5975     insertFirst: function(el, values, returnElement){
5976         return this.doInsert('afterBegin', el, values, returnElement);
5977     },
5978
5979     /**
5980      * Applies the supplied values to the template and inserts the new node(s) before el.
5981      * @param {String/HTMLElement/Roo.Element} el The context element
5982      * @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'})
5983      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5984      * @return {HTMLElement/Roo.Element} The new node or Element
5985      */
5986     insertBefore: function(el, values, returnElement){
5987         return this.doInsert('beforeBegin', el, values, returnElement);
5988     },
5989
5990     /**
5991      * Applies the supplied values to the template and inserts the new node(s) after el.
5992      * @param {String/HTMLElement/Roo.Element} el The context element
5993      * @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'})
5994      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5995      * @return {HTMLElement/Roo.Element} The new node or Element
5996      */
5997     insertAfter : function(el, values, returnElement){
5998         return this.doInsert('afterEnd', el, values, returnElement);
5999     },
6000     
6001     /**
6002      * Applies the supplied values to the template and appends the new node(s) to el.
6003      * @param {String/HTMLElement/Roo.Element} el The context element
6004      * @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'})
6005      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6006      * @return {HTMLElement/Roo.Element} The new node or Element
6007      */
6008     append : function(el, values, returnElement){
6009         return this.doInsert('beforeEnd', el, values, returnElement);
6010     },
6011
6012     doInsert : function(where, el, values, returnEl){
6013         el = Roo.getDom(el);
6014         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6015         return returnEl ? Roo.get(newNode, true) : newNode;
6016     },
6017
6018     /**
6019      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6020      * @param {String/HTMLElement/Roo.Element} el The context element
6021      * @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'})
6022      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6023      * @return {HTMLElement/Roo.Element} The new node or Element
6024      */
6025     overwrite : function(el, values, returnElement){
6026         el = Roo.getDom(el);
6027         el.innerHTML = this.applyTemplate(values);
6028         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6029     }
6030 };
6031 /**
6032  * Alias for {@link #applyTemplate}
6033  * @method
6034  */
6035 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6036
6037 // backwards compat
6038 Roo.DomHelper.Template = Roo.Template;
6039
6040 /**
6041  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6042  * @param {String/HTMLElement} el A DOM element or its id
6043  * @returns {Roo.Template} The created template
6044  * @static
6045  */
6046 Roo.Template.from = function(el){
6047     el = Roo.getDom(el);
6048     return new Roo.Template(el.value || el.innerHTML);
6049 };/*
6050  * Based on:
6051  * Ext JS Library 1.1.1
6052  * Copyright(c) 2006-2007, Ext JS, LLC.
6053  *
6054  * Originally Released Under LGPL - original licence link has changed is not relivant.
6055  *
6056  * Fork - LGPL
6057  * <script type="text/javascript">
6058  */
6059  
6060
6061 /*
6062  * This is code is also distributed under MIT license for use
6063  * with jQuery and prototype JavaScript libraries.
6064  */
6065 /**
6066  * @class Roo.DomQuery
6067 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).
6068 <p>
6069 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>
6070
6071 <p>
6072 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.
6073 </p>
6074 <h4>Element Selectors:</h4>
6075 <ul class="list">
6076     <li> <b>*</b> any element</li>
6077     <li> <b>E</b> an element with the tag E</li>
6078     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6079     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6080     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6081     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6082 </ul>
6083 <h4>Attribute Selectors:</h4>
6084 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6085 <ul class="list">
6086     <li> <b>E[foo]</b> has an attribute "foo"</li>
6087     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6088     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6089     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6090     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6091     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6092     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6093 </ul>
6094 <h4>Pseudo Classes:</h4>
6095 <ul class="list">
6096     <li> <b>E:first-child</b> E is the first child of its parent</li>
6097     <li> <b>E:last-child</b> E is the last child of its parent</li>
6098     <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>
6099     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6100     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6101     <li> <b>E:only-child</b> E is the only child of its parent</li>
6102     <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>
6103     <li> <b>E:first</b> the first E in the resultset</li>
6104     <li> <b>E:last</b> the last E in the resultset</li>
6105     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6106     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6107     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6108     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6109     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6110     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6111     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6112     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6113     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6114 </ul>
6115 <h4>CSS Value Selectors:</h4>
6116 <ul class="list">
6117     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6118     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6119     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6120     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6121     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6122     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6123 </ul>
6124  * @static
6125  */
6126 Roo.DomQuery = function(){
6127     var cache = {}, simpleCache = {}, valueCache = {};
6128     var nonSpace = /\S/;
6129     var trimRe = /^\s+|\s+$/g;
6130     var tplRe = /\{(\d+)\}/g;
6131     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6132     var tagTokenRe = /^(#)?([\w-\*]+)/;
6133     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6134
6135     function child(p, index){
6136         var i = 0;
6137         var n = p.firstChild;
6138         while(n){
6139             if(n.nodeType == 1){
6140                if(++i == index){
6141                    return n;
6142                }
6143             }
6144             n = n.nextSibling;
6145         }
6146         return null;
6147     };
6148
6149     function next(n){
6150         while((n = n.nextSibling) && n.nodeType != 1);
6151         return n;
6152     };
6153
6154     function prev(n){
6155         while((n = n.previousSibling) && n.nodeType != 1);
6156         return n;
6157     };
6158
6159     function children(d){
6160         var n = d.firstChild, ni = -1;
6161             while(n){
6162                 var nx = n.nextSibling;
6163                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6164                     d.removeChild(n);
6165                 }else{
6166                     n.nodeIndex = ++ni;
6167                 }
6168                 n = nx;
6169             }
6170             return this;
6171         };
6172
6173     function byClassName(c, a, v){
6174         if(!v){
6175             return c;
6176         }
6177         var r = [], ri = -1, cn;
6178         for(var i = 0, ci; ci = c[i]; i++){
6179             
6180             
6181             if((' '+
6182                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6183                  +' ').indexOf(v) != -1){
6184                 r[++ri] = ci;
6185             }
6186         }
6187         return r;
6188     };
6189
6190     function attrValue(n, attr){
6191         if(!n.tagName && typeof n.length != "undefined"){
6192             n = n[0];
6193         }
6194         if(!n){
6195             return null;
6196         }
6197         if(attr == "for"){
6198             return n.htmlFor;
6199         }
6200         if(attr == "class" || attr == "className"){
6201             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6202         }
6203         return n.getAttribute(attr) || n[attr];
6204
6205     };
6206
6207     function getNodes(ns, mode, tagName){
6208         var result = [], ri = -1, cs;
6209         if(!ns){
6210             return result;
6211         }
6212         tagName = tagName || "*";
6213         if(typeof ns.getElementsByTagName != "undefined"){
6214             ns = [ns];
6215         }
6216         if(!mode){
6217             for(var i = 0, ni; ni = ns[i]; i++){
6218                 cs = ni.getElementsByTagName(tagName);
6219                 for(var j = 0, ci; ci = cs[j]; j++){
6220                     result[++ri] = ci;
6221                 }
6222             }
6223         }else if(mode == "/" || mode == ">"){
6224             var utag = tagName.toUpperCase();
6225             for(var i = 0, ni, cn; ni = ns[i]; i++){
6226                 cn = ni.children || ni.childNodes;
6227                 for(var j = 0, cj; cj = cn[j]; j++){
6228                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6229                         result[++ri] = cj;
6230                     }
6231                 }
6232             }
6233         }else if(mode == "+"){
6234             var utag = tagName.toUpperCase();
6235             for(var i = 0, n; n = ns[i]; i++){
6236                 while((n = n.nextSibling) && n.nodeType != 1);
6237                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6238                     result[++ri] = n;
6239                 }
6240             }
6241         }else if(mode == "~"){
6242             for(var i = 0, n; n = ns[i]; i++){
6243                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6244                 if(n){
6245                     result[++ri] = n;
6246                 }
6247             }
6248         }
6249         return result;
6250     };
6251
6252     function concat(a, b){
6253         if(b.slice){
6254             return a.concat(b);
6255         }
6256         for(var i = 0, l = b.length; i < l; i++){
6257             a[a.length] = b[i];
6258         }
6259         return a;
6260     }
6261
6262     function byTag(cs, tagName){
6263         if(cs.tagName || cs == document){
6264             cs = [cs];
6265         }
6266         if(!tagName){
6267             return cs;
6268         }
6269         var r = [], ri = -1;
6270         tagName = tagName.toLowerCase();
6271         for(var i = 0, ci; ci = cs[i]; i++){
6272             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6273                 r[++ri] = ci;
6274             }
6275         }
6276         return r;
6277     };
6278
6279     function byId(cs, attr, id){
6280         if(cs.tagName || cs == document){
6281             cs = [cs];
6282         }
6283         if(!id){
6284             return cs;
6285         }
6286         var r = [], ri = -1;
6287         for(var i = 0,ci; ci = cs[i]; i++){
6288             if(ci && ci.id == id){
6289                 r[++ri] = ci;
6290                 return r;
6291             }
6292         }
6293         return r;
6294     };
6295
6296     function byAttribute(cs, attr, value, op, custom){
6297         var r = [], ri = -1, st = custom=="{";
6298         var f = Roo.DomQuery.operators[op];
6299         for(var i = 0, ci; ci = cs[i]; i++){
6300             var a;
6301             if(st){
6302                 a = Roo.DomQuery.getStyle(ci, attr);
6303             }
6304             else if(attr == "class" || attr == "className"){
6305                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6306             }else if(attr == "for"){
6307                 a = ci.htmlFor;
6308             }else if(attr == "href"){
6309                 a = ci.getAttribute("href", 2);
6310             }else{
6311                 a = ci.getAttribute(attr);
6312             }
6313             if((f && f(a, value)) || (!f && a)){
6314                 r[++ri] = ci;
6315             }
6316         }
6317         return r;
6318     };
6319
6320     function byPseudo(cs, name, value){
6321         return Roo.DomQuery.pseudos[name](cs, value);
6322     };
6323
6324     // This is for IE MSXML which does not support expandos.
6325     // IE runs the same speed using setAttribute, however FF slows way down
6326     // and Safari completely fails so they need to continue to use expandos.
6327     var isIE = window.ActiveXObject ? true : false;
6328
6329     // this eval is stop the compressor from
6330     // renaming the variable to something shorter
6331     
6332     /** eval:var:batch */
6333     var batch = 30803; 
6334
6335     var key = 30803;
6336
6337     function nodupIEXml(cs){
6338         var d = ++key;
6339         cs[0].setAttribute("_nodup", d);
6340         var r = [cs[0]];
6341         for(var i = 1, len = cs.length; i < len; i++){
6342             var c = cs[i];
6343             if(!c.getAttribute("_nodup") != d){
6344                 c.setAttribute("_nodup", d);
6345                 r[r.length] = c;
6346             }
6347         }
6348         for(var i = 0, len = cs.length; i < len; i++){
6349             cs[i].removeAttribute("_nodup");
6350         }
6351         return r;
6352     }
6353
6354     function nodup(cs){
6355         if(!cs){
6356             return [];
6357         }
6358         var len = cs.length, c, i, r = cs, cj, ri = -1;
6359         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6360             return cs;
6361         }
6362         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6363             return nodupIEXml(cs);
6364         }
6365         var d = ++key;
6366         cs[0]._nodup = d;
6367         for(i = 1; c = cs[i]; i++){
6368             if(c._nodup != d){
6369                 c._nodup = d;
6370             }else{
6371                 r = [];
6372                 for(var j = 0; j < i; j++){
6373                     r[++ri] = cs[j];
6374                 }
6375                 for(j = i+1; cj = cs[j]; j++){
6376                     if(cj._nodup != d){
6377                         cj._nodup = d;
6378                         r[++ri] = cj;
6379                     }
6380                 }
6381                 return r;
6382             }
6383         }
6384         return r;
6385     }
6386
6387     function quickDiffIEXml(c1, c2){
6388         var d = ++key;
6389         for(var i = 0, len = c1.length; i < len; i++){
6390             c1[i].setAttribute("_qdiff", d);
6391         }
6392         var r = [];
6393         for(var i = 0, len = c2.length; i < len; i++){
6394             if(c2[i].getAttribute("_qdiff") != d){
6395                 r[r.length] = c2[i];
6396             }
6397         }
6398         for(var i = 0, len = c1.length; i < len; i++){
6399            c1[i].removeAttribute("_qdiff");
6400         }
6401         return r;
6402     }
6403
6404     function quickDiff(c1, c2){
6405         var len1 = c1.length;
6406         if(!len1){
6407             return c2;
6408         }
6409         if(isIE && c1[0].selectSingleNode){
6410             return quickDiffIEXml(c1, c2);
6411         }
6412         var d = ++key;
6413         for(var i = 0; i < len1; i++){
6414             c1[i]._qdiff = d;
6415         }
6416         var r = [];
6417         for(var i = 0, len = c2.length; i < len; i++){
6418             if(c2[i]._qdiff != d){
6419                 r[r.length] = c2[i];
6420             }
6421         }
6422         return r;
6423     }
6424
6425     function quickId(ns, mode, root, id){
6426         if(ns == root){
6427            var d = root.ownerDocument || root;
6428            return d.getElementById(id);
6429         }
6430         ns = getNodes(ns, mode, "*");
6431         return byId(ns, null, id);
6432     }
6433
6434     return {
6435         getStyle : function(el, name){
6436             return Roo.fly(el).getStyle(name);
6437         },
6438         /**
6439          * Compiles a selector/xpath query into a reusable function. The returned function
6440          * takes one parameter "root" (optional), which is the context node from where the query should start.
6441          * @param {String} selector The selector/xpath query
6442          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6443          * @return {Function}
6444          */
6445         compile : function(path, type){
6446             type = type || "select";
6447             
6448             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6449             var q = path, mode, lq;
6450             var tk = Roo.DomQuery.matchers;
6451             var tklen = tk.length;
6452             var mm;
6453
6454             // accept leading mode switch
6455             var lmode = q.match(modeRe);
6456             if(lmode && lmode[1]){
6457                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6458                 q = q.replace(lmode[1], "");
6459             }
6460             // strip leading slashes
6461             while(path.substr(0, 1)=="/"){
6462                 path = path.substr(1);
6463             }
6464
6465             while(q && lq != q){
6466                 lq = q;
6467                 var tm = q.match(tagTokenRe);
6468                 if(type == "select"){
6469                     if(tm){
6470                         if(tm[1] == "#"){
6471                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6472                         }else{
6473                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6474                         }
6475                         q = q.replace(tm[0], "");
6476                     }else if(q.substr(0, 1) != '@'){
6477                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6478                     }
6479                 }else{
6480                     if(tm){
6481                         if(tm[1] == "#"){
6482                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6483                         }else{
6484                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6485                         }
6486                         q = q.replace(tm[0], "");
6487                     }
6488                 }
6489                 while(!(mm = q.match(modeRe))){
6490                     var matched = false;
6491                     for(var j = 0; j < tklen; j++){
6492                         var t = tk[j];
6493                         var m = q.match(t.re);
6494                         if(m){
6495                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6496                                                     return m[i];
6497                                                 });
6498                             q = q.replace(m[0], "");
6499                             matched = true;
6500                             break;
6501                         }
6502                     }
6503                     // prevent infinite loop on bad selector
6504                     if(!matched){
6505                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6506                     }
6507                 }
6508                 if(mm[1]){
6509                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6510                     q = q.replace(mm[1], "");
6511                 }
6512             }
6513             fn[fn.length] = "return nodup(n);\n}";
6514             
6515              /** 
6516               * list of variables that need from compression as they are used by eval.
6517              *  eval:var:batch 
6518              *  eval:var:nodup
6519              *  eval:var:byTag
6520              *  eval:var:ById
6521              *  eval:var:getNodes
6522              *  eval:var:quickId
6523              *  eval:var:mode
6524              *  eval:var:root
6525              *  eval:var:n
6526              *  eval:var:byClassName
6527              *  eval:var:byPseudo
6528              *  eval:var:byAttribute
6529              *  eval:var:attrValue
6530              * 
6531              **/ 
6532             eval(fn.join(""));
6533             return f;
6534         },
6535
6536         /**
6537          * Selects a group of elements.
6538          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6539          * @param {Node} root (optional) The start of the query (defaults to document).
6540          * @return {Array}
6541          */
6542         select : function(path, root, type){
6543             if(!root || root == document){
6544                 root = document;
6545             }
6546             if(typeof root == "string"){
6547                 root = document.getElementById(root);
6548             }
6549             var paths = path.split(",");
6550             var results = [];
6551             for(var i = 0, len = paths.length; i < len; i++){
6552                 var p = paths[i].replace(trimRe, "");
6553                 if(!cache[p]){
6554                     cache[p] = Roo.DomQuery.compile(p);
6555                     if(!cache[p]){
6556                         throw p + " is not a valid selector";
6557                     }
6558                 }
6559                 var result = cache[p](root);
6560                 if(result && result != document){
6561                     results = results.concat(result);
6562                 }
6563             }
6564             if(paths.length > 1){
6565                 return nodup(results);
6566             }
6567             return results;
6568         },
6569
6570         /**
6571          * Selects a single element.
6572          * @param {String} selector The selector/xpath query
6573          * @param {Node} root (optional) The start of the query (defaults to document).
6574          * @return {Element}
6575          */
6576         selectNode : function(path, root){
6577             return Roo.DomQuery.select(path, root)[0];
6578         },
6579
6580         /**
6581          * Selects the value of a node, optionally replacing null with the defaultValue.
6582          * @param {String} selector The selector/xpath query
6583          * @param {Node} root (optional) The start of the query (defaults to document).
6584          * @param {String} defaultValue
6585          */
6586         selectValue : function(path, root, defaultValue){
6587             path = path.replace(trimRe, "");
6588             if(!valueCache[path]){
6589                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6590             }
6591             var n = valueCache[path](root);
6592             n = n[0] ? n[0] : n;
6593             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6594             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6595         },
6596
6597         /**
6598          * Selects the value of a node, parsing integers and floats.
6599          * @param {String} selector The selector/xpath query
6600          * @param {Node} root (optional) The start of the query (defaults to document).
6601          * @param {Number} defaultValue
6602          * @return {Number}
6603          */
6604         selectNumber : function(path, root, defaultValue){
6605             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6606             return parseFloat(v);
6607         },
6608
6609         /**
6610          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6611          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6612          * @param {String} selector The simple selector to test
6613          * @return {Boolean}
6614          */
6615         is : function(el, ss){
6616             if(typeof el == "string"){
6617                 el = document.getElementById(el);
6618             }
6619             var isArray = (el instanceof Array);
6620             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6621             return isArray ? (result.length == el.length) : (result.length > 0);
6622         },
6623
6624         /**
6625          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6626          * @param {Array} el An array of elements to filter
6627          * @param {String} selector The simple selector to test
6628          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6629          * the selector instead of the ones that match
6630          * @return {Array}
6631          */
6632         filter : function(els, ss, nonMatches){
6633             ss = ss.replace(trimRe, "");
6634             if(!simpleCache[ss]){
6635                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6636             }
6637             var result = simpleCache[ss](els);
6638             return nonMatches ? quickDiff(result, els) : result;
6639         },
6640
6641         /**
6642          * Collection of matching regular expressions and code snippets.
6643          */
6644         matchers : [{
6645                 re: /^\.([\w-]+)/,
6646                 select: 'n = byClassName(n, null, " {1} ");'
6647             }, {
6648                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6649                 select: 'n = byPseudo(n, "{1}", "{2}");'
6650             },{
6651                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6652                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6653             }, {
6654                 re: /^#([\w-]+)/,
6655                 select: 'n = byId(n, null, "{1}");'
6656             },{
6657                 re: /^@([\w-]+)/,
6658                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6659             }
6660         ],
6661
6662         /**
6663          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6664          * 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;.
6665          */
6666         operators : {
6667             "=" : function(a, v){
6668                 return a == v;
6669             },
6670             "!=" : function(a, v){
6671                 return a != v;
6672             },
6673             "^=" : function(a, v){
6674                 return a && a.substr(0, v.length) == v;
6675             },
6676             "$=" : function(a, v){
6677                 return a && a.substr(a.length-v.length) == v;
6678             },
6679             "*=" : function(a, v){
6680                 return a && a.indexOf(v) !== -1;
6681             },
6682             "%=" : function(a, v){
6683                 return (a % v) == 0;
6684             },
6685             "|=" : function(a, v){
6686                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6687             },
6688             "~=" : function(a, v){
6689                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6690             }
6691         },
6692
6693         /**
6694          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6695          * and the argument (if any) supplied in the selector.
6696          */
6697         pseudos : {
6698             "first-child" : function(c){
6699                 var r = [], ri = -1, n;
6700                 for(var i = 0, ci; ci = n = c[i]; i++){
6701                     while((n = n.previousSibling) && n.nodeType != 1);
6702                     if(!n){
6703                         r[++ri] = ci;
6704                     }
6705                 }
6706                 return r;
6707             },
6708
6709             "last-child" : function(c){
6710                 var r = [], ri = -1, n;
6711                 for(var i = 0, ci; ci = n = c[i]; i++){
6712                     while((n = n.nextSibling) && n.nodeType != 1);
6713                     if(!n){
6714                         r[++ri] = ci;
6715                     }
6716                 }
6717                 return r;
6718             },
6719
6720             "nth-child" : function(c, a) {
6721                 var r = [], ri = -1;
6722                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6723                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6724                 for(var i = 0, n; n = c[i]; i++){
6725                     var pn = n.parentNode;
6726                     if (batch != pn._batch) {
6727                         var j = 0;
6728                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6729                             if(cn.nodeType == 1){
6730                                cn.nodeIndex = ++j;
6731                             }
6732                         }
6733                         pn._batch = batch;
6734                     }
6735                     if (f == 1) {
6736                         if (l == 0 || n.nodeIndex == l){
6737                             r[++ri] = n;
6738                         }
6739                     } else if ((n.nodeIndex + l) % f == 0){
6740                         r[++ri] = n;
6741                     }
6742                 }
6743
6744                 return r;
6745             },
6746
6747             "only-child" : function(c){
6748                 var r = [], ri = -1;;
6749                 for(var i = 0, ci; ci = c[i]; i++){
6750                     if(!prev(ci) && !next(ci)){
6751                         r[++ri] = ci;
6752                     }
6753                 }
6754                 return r;
6755             },
6756
6757             "empty" : function(c){
6758                 var r = [], ri = -1;
6759                 for(var i = 0, ci; ci = c[i]; i++){
6760                     var cns = ci.childNodes, j = 0, cn, empty = true;
6761                     while(cn = cns[j]){
6762                         ++j;
6763                         if(cn.nodeType == 1 || cn.nodeType == 3){
6764                             empty = false;
6765                             break;
6766                         }
6767                     }
6768                     if(empty){
6769                         r[++ri] = ci;
6770                     }
6771                 }
6772                 return r;
6773             },
6774
6775             "contains" : function(c, v){
6776                 var r = [], ri = -1;
6777                 for(var i = 0, ci; ci = c[i]; i++){
6778                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6779                         r[++ri] = ci;
6780                     }
6781                 }
6782                 return r;
6783             },
6784
6785             "nodeValue" : function(c, v){
6786                 var r = [], ri = -1;
6787                 for(var i = 0, ci; ci = c[i]; i++){
6788                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6789                         r[++ri] = ci;
6790                     }
6791                 }
6792                 return r;
6793             },
6794
6795             "checked" : function(c){
6796                 var r = [], ri = -1;
6797                 for(var i = 0, ci; ci = c[i]; i++){
6798                     if(ci.checked == true){
6799                         r[++ri] = ci;
6800                     }
6801                 }
6802                 return r;
6803             },
6804
6805             "not" : function(c, ss){
6806                 return Roo.DomQuery.filter(c, ss, true);
6807             },
6808
6809             "odd" : function(c){
6810                 return this["nth-child"](c, "odd");
6811             },
6812
6813             "even" : function(c){
6814                 return this["nth-child"](c, "even");
6815             },
6816
6817             "nth" : function(c, a){
6818                 return c[a-1] || [];
6819             },
6820
6821             "first" : function(c){
6822                 return c[0] || [];
6823             },
6824
6825             "last" : function(c){
6826                 return c[c.length-1] || [];
6827             },
6828
6829             "has" : function(c, ss){
6830                 var s = Roo.DomQuery.select;
6831                 var r = [], ri = -1;
6832                 for(var i = 0, ci; ci = c[i]; i++){
6833                     if(s(ss, ci).length > 0){
6834                         r[++ri] = ci;
6835                     }
6836                 }
6837                 return r;
6838             },
6839
6840             "next" : function(c, ss){
6841                 var is = Roo.DomQuery.is;
6842                 var r = [], ri = -1;
6843                 for(var i = 0, ci; ci = c[i]; i++){
6844                     var n = next(ci);
6845                     if(n && is(n, ss)){
6846                         r[++ri] = ci;
6847                     }
6848                 }
6849                 return r;
6850             },
6851
6852             "prev" : function(c, ss){
6853                 var is = Roo.DomQuery.is;
6854                 var r = [], ri = -1;
6855                 for(var i = 0, ci; ci = c[i]; i++){
6856                     var n = prev(ci);
6857                     if(n && is(n, ss)){
6858                         r[++ri] = ci;
6859                     }
6860                 }
6861                 return r;
6862             }
6863         }
6864     };
6865 }();
6866
6867 /**
6868  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6869  * @param {String} path The selector/xpath query
6870  * @param {Node} root (optional) The start of the query (defaults to document).
6871  * @return {Array}
6872  * @member Roo
6873  * @method query
6874  */
6875 Roo.query = Roo.DomQuery.select;
6876 /*
6877  * Based on:
6878  * Ext JS Library 1.1.1
6879  * Copyright(c) 2006-2007, Ext JS, LLC.
6880  *
6881  * Originally Released Under LGPL - original licence link has changed is not relivant.
6882  *
6883  * Fork - LGPL
6884  * <script type="text/javascript">
6885  */
6886
6887 /**
6888  * @class Roo.util.Observable
6889  * Base class that provides a common interface for publishing events. Subclasses are expected to
6890  * to have a property "events" with all the events defined.<br>
6891  * For example:
6892  * <pre><code>
6893  Employee = function(name){
6894     this.name = name;
6895     this.addEvents({
6896         "fired" : true,
6897         "quit" : true
6898     });
6899  }
6900  Roo.extend(Employee, Roo.util.Observable);
6901 </code></pre>
6902  * @param {Object} config properties to use (incuding events / listeners)
6903  */
6904
6905 Roo.util.Observable = function(cfg){
6906     
6907     cfg = cfg|| {};
6908     this.addEvents(cfg.events || {});
6909     if (cfg.events) {
6910         delete cfg.events; // make sure
6911     }
6912      
6913     Roo.apply(this, cfg);
6914     
6915     if(this.listeners){
6916         this.on(this.listeners);
6917         delete this.listeners;
6918     }
6919 };
6920 Roo.util.Observable.prototype = {
6921     /** 
6922  * @cfg {Object} listeners  list of events and functions to call for this object, 
6923  * For example :
6924  * <pre><code>
6925     listeners :  { 
6926        'click' : function(e) {
6927            ..... 
6928         } ,
6929         .... 
6930     } 
6931   </code></pre>
6932  */
6933     
6934     
6935     /**
6936      * Fires the specified event with the passed parameters (minus the event name).
6937      * @param {String} eventName
6938      * @param {Object...} args Variable number of parameters are passed to handlers
6939      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6940      */
6941     fireEvent : function(){
6942         var ce = this.events[arguments[0].toLowerCase()];
6943         if(typeof ce == "object"){
6944             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6945         }else{
6946             return true;
6947         }
6948     },
6949
6950     // private
6951     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6952
6953     /**
6954      * Appends an event handler to this component
6955      * @param {String}   eventName The type of event to listen for
6956      * @param {Function} handler The method the event invokes
6957      * @param {Object}   scope (optional) The scope in which to execute the handler
6958      * function. The handler function's "this" context.
6959      * @param {Object}   options (optional) An object containing handler configuration
6960      * properties. This may contain any of the following properties:<ul>
6961      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6962      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6963      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6964      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6965      * by the specified number of milliseconds. If the event fires again within that time, the original
6966      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6967      * </ul><br>
6968      * <p>
6969      * <b>Combining Options</b><br>
6970      * Using the options argument, it is possible to combine different types of listeners:<br>
6971      * <br>
6972      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6973                 <pre><code>
6974                 el.on('click', this.onClick, this, {
6975                         single: true,
6976                 delay: 100,
6977                 forumId: 4
6978                 });
6979                 </code></pre>
6980      * <p>
6981      * <b>Attaching multiple handlers in 1 call</b><br>
6982      * The method also allows for a single argument to be passed which is a config object containing properties
6983      * which specify multiple handlers.
6984      * <pre><code>
6985                 el.on({
6986                         'click': {
6987                         fn: this.onClick,
6988                         scope: this,
6989                         delay: 100
6990                 }, 
6991                 'mouseover': {
6992                         fn: this.onMouseOver,
6993                         scope: this
6994                 },
6995                 'mouseout': {
6996                         fn: this.onMouseOut,
6997                         scope: this
6998                 }
6999                 });
7000                 </code></pre>
7001      * <p>
7002      * Or a shorthand syntax which passes the same scope object to all handlers:
7003         <pre><code>
7004                 el.on({
7005                         'click': this.onClick,
7006                 'mouseover': this.onMouseOver,
7007                 'mouseout': this.onMouseOut,
7008                 scope: this
7009                 });
7010                 </code></pre>
7011      */
7012     addListener : function(eventName, fn, scope, o){
7013         if(typeof eventName == "object"){
7014             o = eventName;
7015             for(var e in o){
7016                 if(this.filterOptRe.test(e)){
7017                     continue;
7018                 }
7019                 if(typeof o[e] == "function"){
7020                     // shared options
7021                     this.addListener(e, o[e], o.scope,  o);
7022                 }else{
7023                     // individual options
7024                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7025                 }
7026             }
7027             return;
7028         }
7029         o = (!o || typeof o == "boolean") ? {} : o;
7030         eventName = eventName.toLowerCase();
7031         var ce = this.events[eventName] || true;
7032         if(typeof ce == "boolean"){
7033             ce = new Roo.util.Event(this, eventName);
7034             this.events[eventName] = ce;
7035         }
7036         ce.addListener(fn, scope, o);
7037     },
7038
7039     /**
7040      * Removes a listener
7041      * @param {String}   eventName     The type of event to listen for
7042      * @param {Function} handler        The handler to remove
7043      * @param {Object}   scope  (optional) The scope (this object) for the handler
7044      */
7045     removeListener : function(eventName, fn, scope){
7046         var ce = this.events[eventName.toLowerCase()];
7047         if(typeof ce == "object"){
7048             ce.removeListener(fn, scope);
7049         }
7050     },
7051
7052     /**
7053      * Removes all listeners for this object
7054      */
7055     purgeListeners : function(){
7056         for(var evt in this.events){
7057             if(typeof this.events[evt] == "object"){
7058                  this.events[evt].clearListeners();
7059             }
7060         }
7061     },
7062
7063     relayEvents : function(o, events){
7064         var createHandler = function(ename){
7065             return function(){
7066                  
7067                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7068             };
7069         };
7070         for(var i = 0, len = events.length; i < len; i++){
7071             var ename = events[i];
7072             if(!this.events[ename]){
7073                 this.events[ename] = true;
7074             };
7075             o.on(ename, createHandler(ename), this);
7076         }
7077     },
7078
7079     /**
7080      * Used to define events on this Observable
7081      * @param {Object} object The object with the events defined
7082      */
7083     addEvents : function(o){
7084         if(!this.events){
7085             this.events = {};
7086         }
7087         Roo.applyIf(this.events, o);
7088     },
7089
7090     /**
7091      * Checks to see if this object has any listeners for a specified event
7092      * @param {String} eventName The name of the event to check for
7093      * @return {Boolean} True if the event is being listened for, else false
7094      */
7095     hasListener : function(eventName){
7096         var e = this.events[eventName];
7097         return typeof e == "object" && e.listeners.length > 0;
7098     }
7099 };
7100 /**
7101  * Appends an event handler to this element (shorthand for addListener)
7102  * @param {String}   eventName     The type of event to listen for
7103  * @param {Function} handler        The method the event invokes
7104  * @param {Object}   scope (optional) The scope in which to execute the handler
7105  * function. The handler function's "this" context.
7106  * @param {Object}   options  (optional)
7107  * @method
7108  */
7109 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7110 /**
7111  * Removes a listener (shorthand for removeListener)
7112  * @param {String}   eventName     The type of event to listen for
7113  * @param {Function} handler        The handler to remove
7114  * @param {Object}   scope  (optional) The scope (this object) for the handler
7115  * @method
7116  */
7117 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7118
7119 /**
7120  * Starts capture on the specified Observable. All events will be passed
7121  * to the supplied function with the event name + standard signature of the event
7122  * <b>before</b> the event is fired. If the supplied function returns false,
7123  * the event will not fire.
7124  * @param {Observable} o The Observable to capture
7125  * @param {Function} fn The function to call
7126  * @param {Object} scope (optional) The scope (this object) for the fn
7127  * @static
7128  */
7129 Roo.util.Observable.capture = function(o, fn, scope){
7130     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7131 };
7132
7133 /**
7134  * Removes <b>all</b> added captures from the Observable.
7135  * @param {Observable} o The Observable to release
7136  * @static
7137  */
7138 Roo.util.Observable.releaseCapture = function(o){
7139     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7140 };
7141
7142 (function(){
7143
7144     var createBuffered = function(h, o, scope){
7145         var task = new Roo.util.DelayedTask();
7146         return function(){
7147             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7148         };
7149     };
7150
7151     var createSingle = function(h, e, fn, scope){
7152         return function(){
7153             e.removeListener(fn, scope);
7154             return h.apply(scope, arguments);
7155         };
7156     };
7157
7158     var createDelayed = function(h, o, scope){
7159         return function(){
7160             var args = Array.prototype.slice.call(arguments, 0);
7161             setTimeout(function(){
7162                 h.apply(scope, args);
7163             }, o.delay || 10);
7164         };
7165     };
7166
7167     Roo.util.Event = function(obj, name){
7168         this.name = name;
7169         this.obj = obj;
7170         this.listeners = [];
7171     };
7172
7173     Roo.util.Event.prototype = {
7174         addListener : function(fn, scope, options){
7175             var o = options || {};
7176             scope = scope || this.obj;
7177             if(!this.isListening(fn, scope)){
7178                 var l = {fn: fn, scope: scope, options: o};
7179                 var h = fn;
7180                 if(o.delay){
7181                     h = createDelayed(h, o, scope);
7182                 }
7183                 if(o.single){
7184                     h = createSingle(h, this, fn, scope);
7185                 }
7186                 if(o.buffer){
7187                     h = createBuffered(h, o, scope);
7188                 }
7189                 l.fireFn = h;
7190                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7191                     this.listeners.push(l);
7192                 }else{
7193                     this.listeners = this.listeners.slice(0);
7194                     this.listeners.push(l);
7195                 }
7196             }
7197         },
7198
7199         findListener : function(fn, scope){
7200             scope = scope || this.obj;
7201             var ls = this.listeners;
7202             for(var i = 0, len = ls.length; i < len; i++){
7203                 var l = ls[i];
7204                 if(l.fn == fn && l.scope == scope){
7205                     return i;
7206                 }
7207             }
7208             return -1;
7209         },
7210
7211         isListening : function(fn, scope){
7212             return this.findListener(fn, scope) != -1;
7213         },
7214
7215         removeListener : function(fn, scope){
7216             var index;
7217             if((index = this.findListener(fn, scope)) != -1){
7218                 if(!this.firing){
7219                     this.listeners.splice(index, 1);
7220                 }else{
7221                     this.listeners = this.listeners.slice(0);
7222                     this.listeners.splice(index, 1);
7223                 }
7224                 return true;
7225             }
7226             return false;
7227         },
7228
7229         clearListeners : function(){
7230             this.listeners = [];
7231         },
7232
7233         fire : function(){
7234             var ls = this.listeners, scope, len = ls.length;
7235             if(len > 0){
7236                 this.firing = true;
7237                 var args = Array.prototype.slice.call(arguments, 0);                
7238                 for(var i = 0; i < len; i++){
7239                     var l = ls[i];
7240                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7241                         this.firing = false;
7242                         return false;
7243                     }
7244                 }
7245                 this.firing = false;
7246             }
7247             return true;
7248         }
7249     };
7250 })();/*
7251  * RooJS Library 
7252  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7253  *
7254  * Licence LGPL 
7255  *
7256  */
7257  
7258 /**
7259  * @class Roo.Document
7260  * @extends Roo.util.Observable
7261  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7262  * 
7263  * @param {Object} config the methods and properties of the 'base' class for the application.
7264  * 
7265  *  Generic Page handler - implement this to start your app..
7266  * 
7267  * eg.
7268  *  MyProject = new Roo.Document({
7269         events : {
7270             'load' : true // your events..
7271         },
7272         listeners : {
7273             'ready' : function() {
7274                 // fired on Roo.onReady()
7275             }
7276         }
7277  * 
7278  */
7279 Roo.Document = function(cfg) {
7280      
7281     this.addEvents({ 
7282         'ready' : true
7283     });
7284     Roo.util.Observable.call(this,cfg);
7285     
7286     var _this = this;
7287     
7288     Roo.onReady(function() {
7289         _this.fireEvent('ready');
7290     },null,false);
7291     
7292     
7293 }
7294
7295 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7296  * Based on:
7297  * Ext JS Library 1.1.1
7298  * Copyright(c) 2006-2007, Ext JS, LLC.
7299  *
7300  * Originally Released Under LGPL - original licence link has changed is not relivant.
7301  *
7302  * Fork - LGPL
7303  * <script type="text/javascript">
7304  */
7305
7306 /**
7307  * @class Roo.EventManager
7308  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7309  * several useful events directly.
7310  * See {@link Roo.EventObject} for more details on normalized event objects.
7311  * @static
7312  */
7313 Roo.EventManager = function(){
7314     var docReadyEvent, docReadyProcId, docReadyState = false;
7315     var resizeEvent, resizeTask, textEvent, textSize;
7316     var E = Roo.lib.Event;
7317     var D = Roo.lib.Dom;
7318
7319     
7320     
7321
7322     var fireDocReady = function(){
7323         if(!docReadyState){
7324             docReadyState = true;
7325             Roo.isReady = true;
7326             if(docReadyProcId){
7327                 clearInterval(docReadyProcId);
7328             }
7329             if(Roo.isGecko || Roo.isOpera) {
7330                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7331             }
7332             if(Roo.isIE){
7333                 var defer = document.getElementById("ie-deferred-loader");
7334                 if(defer){
7335                     defer.onreadystatechange = null;
7336                     defer.parentNode.removeChild(defer);
7337                 }
7338             }
7339             if(docReadyEvent){
7340                 docReadyEvent.fire();
7341                 docReadyEvent.clearListeners();
7342             }
7343         }
7344     };
7345     
7346     var initDocReady = function(){
7347         docReadyEvent = new Roo.util.Event();
7348         if(Roo.isGecko || Roo.isOpera) {
7349             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7350         }else if(Roo.isIE){
7351             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7352             var defer = document.getElementById("ie-deferred-loader");
7353             defer.onreadystatechange = function(){
7354                 if(this.readyState == "complete"){
7355                     fireDocReady();
7356                 }
7357             };
7358         }else if(Roo.isSafari){ 
7359             docReadyProcId = setInterval(function(){
7360                 var rs = document.readyState;
7361                 if(rs == "complete") {
7362                     fireDocReady();     
7363                  }
7364             }, 10);
7365         }
7366         // no matter what, make sure it fires on load
7367         E.on(window, "load", fireDocReady);
7368     };
7369
7370     var createBuffered = function(h, o){
7371         var task = new Roo.util.DelayedTask(h);
7372         return function(e){
7373             // create new event object impl so new events don't wipe out properties
7374             e = new Roo.EventObjectImpl(e);
7375             task.delay(o.buffer, h, null, [e]);
7376         };
7377     };
7378
7379     var createSingle = function(h, el, ename, fn){
7380         return function(e){
7381             Roo.EventManager.removeListener(el, ename, fn);
7382             h(e);
7383         };
7384     };
7385
7386     var createDelayed = function(h, o){
7387         return function(e){
7388             // create new event object impl so new events don't wipe out properties
7389             e = new Roo.EventObjectImpl(e);
7390             setTimeout(function(){
7391                 h(e);
7392             }, o.delay || 10);
7393         };
7394     };
7395     var transitionEndVal = false;
7396     
7397     var transitionEnd = function()
7398     {
7399         if (transitionEndVal) {
7400             return transitionEndVal;
7401         }
7402         var el = document.createElement('div');
7403
7404         var transEndEventNames = {
7405             WebkitTransition : 'webkitTransitionEnd',
7406             MozTransition    : 'transitionend',
7407             OTransition      : 'oTransitionEnd otransitionend',
7408             transition       : 'transitionend'
7409         };
7410     
7411         for (var name in transEndEventNames) {
7412             if (el.style[name] !== undefined) {
7413                 transitionEndVal = transEndEventNames[name];
7414                 return  transitionEndVal ;
7415             }
7416         }
7417     }
7418     
7419   
7420
7421     var listen = function(element, ename, opt, fn, scope)
7422     {
7423         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7424         fn = fn || o.fn; scope = scope || o.scope;
7425         var el = Roo.getDom(element);
7426         
7427         
7428         if(!el){
7429             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7430         }
7431         
7432         if (ename == 'transitionend') {
7433             ename = transitionEnd();
7434         }
7435         var h = function(e){
7436             e = Roo.EventObject.setEvent(e);
7437             var t;
7438             if(o.delegate){
7439                 t = e.getTarget(o.delegate, el);
7440                 if(!t){
7441                     return;
7442                 }
7443             }else{
7444                 t = e.target;
7445             }
7446             if(o.stopEvent === true){
7447                 e.stopEvent();
7448             }
7449             if(o.preventDefault === true){
7450                e.preventDefault();
7451             }
7452             if(o.stopPropagation === true){
7453                 e.stopPropagation();
7454             }
7455
7456             if(o.normalized === false){
7457                 e = e.browserEvent;
7458             }
7459
7460             fn.call(scope || el, e, t, o);
7461         };
7462         if(o.delay){
7463             h = createDelayed(h, o);
7464         }
7465         if(o.single){
7466             h = createSingle(h, el, ename, fn);
7467         }
7468         if(o.buffer){
7469             h = createBuffered(h, o);
7470         }
7471         
7472         fn._handlers = fn._handlers || [];
7473         
7474         
7475         fn._handlers.push([Roo.id(el), ename, h]);
7476         
7477         
7478          
7479         E.on(el, ename, h); // this adds the actuall listener to the object..
7480         
7481         
7482         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7483             el.addEventListener("DOMMouseScroll", h, false);
7484             E.on(window, 'unload', function(){
7485                 el.removeEventListener("DOMMouseScroll", h, false);
7486             });
7487         }
7488         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7489             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7490         }
7491         return h;
7492     };
7493
7494     var stopListening = function(el, ename, fn){
7495         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7496         if(hds){
7497             for(var i = 0, len = hds.length; i < len; i++){
7498                 var h = hds[i];
7499                 if(h[0] == id && h[1] == ename){
7500                     hd = h[2];
7501                     hds.splice(i, 1);
7502                     break;
7503                 }
7504             }
7505         }
7506         E.un(el, ename, hd);
7507         el = Roo.getDom(el);
7508         if(ename == "mousewheel" && el.addEventListener){
7509             el.removeEventListener("DOMMouseScroll", hd, false);
7510         }
7511         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7512             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7513         }
7514     };
7515
7516     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7517     
7518     var pub = {
7519         
7520         
7521         /** 
7522          * Fix for doc tools
7523          * @scope Roo.EventManager
7524          */
7525         
7526         
7527         /** 
7528          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7529          * object with a Roo.EventObject
7530          * @param {Function} fn        The method the event invokes
7531          * @param {Object}   scope    An object that becomes the scope of the handler
7532          * @param {boolean}  override If true, the obj passed in becomes
7533          *                             the execution scope of the listener
7534          * @return {Function} The wrapped function
7535          * @deprecated
7536          */
7537         wrap : function(fn, scope, override){
7538             return function(e){
7539                 Roo.EventObject.setEvent(e);
7540                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7541             };
7542         },
7543         
7544         /**
7545      * Appends an event handler to an element (shorthand for addListener)
7546      * @param {String/HTMLElement}   element        The html element or id to assign the
7547      * @param {String}   eventName The type of event to listen for
7548      * @param {Function} handler The method the event invokes
7549      * @param {Object}   scope (optional) The scope in which to execute the handler
7550      * function. The handler function's "this" context.
7551      * @param {Object}   options (optional) An object containing handler configuration
7552      * properties. This may contain any of the following properties:<ul>
7553      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7554      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7555      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7556      * <li>preventDefault {Boolean} True to prevent the default action</li>
7557      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7558      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7559      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7560      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7561      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7562      * by the specified number of milliseconds. If the event fires again within that time, the original
7563      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7564      * </ul><br>
7565      * <p>
7566      * <b>Combining Options</b><br>
7567      * Using the options argument, it is possible to combine different types of listeners:<br>
7568      * <br>
7569      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7570      * Code:<pre><code>
7571 el.on('click', this.onClick, this, {
7572     single: true,
7573     delay: 100,
7574     stopEvent : true,
7575     forumId: 4
7576 });</code></pre>
7577      * <p>
7578      * <b>Attaching multiple handlers in 1 call</b><br>
7579       * The method also allows for a single argument to be passed which is a config object containing properties
7580      * which specify multiple handlers.
7581      * <p>
7582      * Code:<pre><code>
7583 el.on({
7584     'click' : {
7585         fn: this.onClick
7586         scope: this,
7587         delay: 100
7588     },
7589     'mouseover' : {
7590         fn: this.onMouseOver
7591         scope: this
7592     },
7593     'mouseout' : {
7594         fn: this.onMouseOut
7595         scope: this
7596     }
7597 });</code></pre>
7598      * <p>
7599      * Or a shorthand syntax:<br>
7600      * Code:<pre><code>
7601 el.on({
7602     'click' : this.onClick,
7603     'mouseover' : this.onMouseOver,
7604     'mouseout' : this.onMouseOut
7605     scope: this
7606 });</code></pre>
7607      */
7608         addListener : function(element, eventName, fn, scope, options){
7609             if(typeof eventName == "object"){
7610                 var o = eventName;
7611                 for(var e in o){
7612                     if(propRe.test(e)){
7613                         continue;
7614                     }
7615                     if(typeof o[e] == "function"){
7616                         // shared options
7617                         listen(element, e, o, o[e], o.scope);
7618                     }else{
7619                         // individual options
7620                         listen(element, e, o[e]);
7621                     }
7622                 }
7623                 return;
7624             }
7625             return listen(element, eventName, options, fn, scope);
7626         },
7627         
7628         /**
7629          * Removes an event handler
7630          *
7631          * @param {String/HTMLElement}   element        The id or html element to remove the 
7632          *                             event from
7633          * @param {String}   eventName     The type of event
7634          * @param {Function} fn
7635          * @return {Boolean} True if a listener was actually removed
7636          */
7637         removeListener : function(element, eventName, fn){
7638             return stopListening(element, eventName, fn);
7639         },
7640         
7641         /**
7642          * Fires when the document is ready (before onload and before images are loaded). Can be 
7643          * accessed shorthanded Roo.onReady().
7644          * @param {Function} fn        The method the event invokes
7645          * @param {Object}   scope    An  object that becomes the scope of the handler
7646          * @param {boolean}  options
7647          */
7648         onDocumentReady : function(fn, scope, options){
7649             if(docReadyState){ // if it already fired
7650                 docReadyEvent.addListener(fn, scope, options);
7651                 docReadyEvent.fire();
7652                 docReadyEvent.clearListeners();
7653                 return;
7654             }
7655             if(!docReadyEvent){
7656                 initDocReady();
7657             }
7658             docReadyEvent.addListener(fn, scope, options);
7659         },
7660         
7661         /**
7662          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7663          * @param {Function} fn        The method the event invokes
7664          * @param {Object}   scope    An object that becomes the scope of the handler
7665          * @param {boolean}  options
7666          */
7667         onWindowResize : function(fn, scope, options)
7668         {
7669             if(!resizeEvent){
7670                 resizeEvent = new Roo.util.Event();
7671                 resizeTask = new Roo.util.DelayedTask(function(){
7672                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7673                 });
7674                 E.on(window, "resize", function()
7675                 {
7676                     if (Roo.isIE) {
7677                         resizeTask.delay(50);
7678                     } else {
7679                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7680                     }
7681                 });
7682             }
7683             resizeEvent.addListener(fn, scope, options);
7684         },
7685
7686         /**
7687          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7688          * @param {Function} fn        The method the event invokes
7689          * @param {Object}   scope    An object that becomes the scope of the handler
7690          * @param {boolean}  options
7691          */
7692         onTextResize : function(fn, scope, options){
7693             if(!textEvent){
7694                 textEvent = new Roo.util.Event();
7695                 var textEl = new Roo.Element(document.createElement('div'));
7696                 textEl.dom.className = 'x-text-resize';
7697                 textEl.dom.innerHTML = 'X';
7698                 textEl.appendTo(document.body);
7699                 textSize = textEl.dom.offsetHeight;
7700                 setInterval(function(){
7701                     if(textEl.dom.offsetHeight != textSize){
7702                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7703                     }
7704                 }, this.textResizeInterval);
7705             }
7706             textEvent.addListener(fn, scope, options);
7707         },
7708
7709         /**
7710          * Removes the passed window resize listener.
7711          * @param {Function} fn        The method the event invokes
7712          * @param {Object}   scope    The scope of handler
7713          */
7714         removeResizeListener : function(fn, scope){
7715             if(resizeEvent){
7716                 resizeEvent.removeListener(fn, scope);
7717             }
7718         },
7719
7720         // private
7721         fireResize : function(){
7722             if(resizeEvent){
7723                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7724             }   
7725         },
7726         /**
7727          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7728          */
7729         ieDeferSrc : false,
7730         /**
7731          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7732          */
7733         textResizeInterval : 50
7734     };
7735     
7736     /**
7737      * Fix for doc tools
7738      * @scopeAlias pub=Roo.EventManager
7739      */
7740     
7741      /**
7742      * Appends an event handler to an element (shorthand for addListener)
7743      * @param {String/HTMLElement}   element        The html element or id to assign the
7744      * @param {String}   eventName The type of event to listen for
7745      * @param {Function} handler The method the event invokes
7746      * @param {Object}   scope (optional) The scope in which to execute the handler
7747      * function. The handler function's "this" context.
7748      * @param {Object}   options (optional) An object containing handler configuration
7749      * properties. This may contain any of the following properties:<ul>
7750      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7751      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7752      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7753      * <li>preventDefault {Boolean} True to prevent the default action</li>
7754      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7755      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7756      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7757      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7758      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7759      * by the specified number of milliseconds. If the event fires again within that time, the original
7760      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7761      * </ul><br>
7762      * <p>
7763      * <b>Combining Options</b><br>
7764      * Using the options argument, it is possible to combine different types of listeners:<br>
7765      * <br>
7766      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7767      * Code:<pre><code>
7768 el.on('click', this.onClick, this, {
7769     single: true,
7770     delay: 100,
7771     stopEvent : true,
7772     forumId: 4
7773 });</code></pre>
7774      * <p>
7775      * <b>Attaching multiple handlers in 1 call</b><br>
7776       * The method also allows for a single argument to be passed which is a config object containing properties
7777      * which specify multiple handlers.
7778      * <p>
7779      * Code:<pre><code>
7780 el.on({
7781     'click' : {
7782         fn: this.onClick
7783         scope: this,
7784         delay: 100
7785     },
7786     'mouseover' : {
7787         fn: this.onMouseOver
7788         scope: this
7789     },
7790     'mouseout' : {
7791         fn: this.onMouseOut
7792         scope: this
7793     }
7794 });</code></pre>
7795      * <p>
7796      * Or a shorthand syntax:<br>
7797      * Code:<pre><code>
7798 el.on({
7799     'click' : this.onClick,
7800     'mouseover' : this.onMouseOver,
7801     'mouseout' : this.onMouseOut
7802     scope: this
7803 });</code></pre>
7804      */
7805     pub.on = pub.addListener;
7806     pub.un = pub.removeListener;
7807
7808     pub.stoppedMouseDownEvent = new Roo.util.Event();
7809     return pub;
7810 }();
7811 /**
7812   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7813   * @param {Function} fn        The method the event invokes
7814   * @param {Object}   scope    An  object that becomes the scope of the handler
7815   * @param {boolean}  override If true, the obj passed in becomes
7816   *                             the execution scope of the listener
7817   * @member Roo
7818   * @method onReady
7819  */
7820 Roo.onReady = Roo.EventManager.onDocumentReady;
7821
7822 Roo.onReady(function(){
7823     var bd = Roo.get(document.body);
7824     if(!bd){ return; }
7825
7826     var cls = [
7827             Roo.isIE ? "roo-ie"
7828             : Roo.isIE11 ? "roo-ie11"
7829             : Roo.isEdge ? "roo-edge"
7830             : Roo.isGecko ? "roo-gecko"
7831             : Roo.isOpera ? "roo-opera"
7832             : Roo.isSafari ? "roo-safari" : ""];
7833
7834     if(Roo.isMac){
7835         cls.push("roo-mac");
7836     }
7837     if(Roo.isLinux){
7838         cls.push("roo-linux");
7839     }
7840     if(Roo.isIOS){
7841         cls.push("roo-ios");
7842     }
7843     if(Roo.isTouch){
7844         cls.push("roo-touch");
7845     }
7846     if(Roo.isBorderBox){
7847         cls.push('roo-border-box');
7848     }
7849     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7850         var p = bd.dom.parentNode;
7851         if(p){
7852             p.className += ' roo-strict';
7853         }
7854     }
7855     bd.addClass(cls.join(' '));
7856 });
7857
7858 /**
7859  * @class Roo.EventObject
7860  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7861  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7862  * Example:
7863  * <pre><code>
7864  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7865     e.preventDefault();
7866     var target = e.getTarget();
7867     ...
7868  }
7869  var myDiv = Roo.get("myDiv");
7870  myDiv.on("click", handleClick);
7871  //or
7872  Roo.EventManager.on("myDiv", 'click', handleClick);
7873  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7874  </code></pre>
7875  * @static
7876  */
7877 Roo.EventObject = function(){
7878     
7879     var E = Roo.lib.Event;
7880     
7881     // safari keypress events for special keys return bad keycodes
7882     var safariKeys = {
7883         63234 : 37, // left
7884         63235 : 39, // right
7885         63232 : 38, // up
7886         63233 : 40, // down
7887         63276 : 33, // page up
7888         63277 : 34, // page down
7889         63272 : 46, // delete
7890         63273 : 36, // home
7891         63275 : 35  // end
7892     };
7893
7894     // normalize button clicks
7895     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7896                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7897
7898     Roo.EventObjectImpl = function(e){
7899         if(e){
7900             this.setEvent(e.browserEvent || e);
7901         }
7902     };
7903     Roo.EventObjectImpl.prototype = {
7904         /**
7905          * Used to fix doc tools.
7906          * @scope Roo.EventObject.prototype
7907          */
7908             
7909
7910         
7911         
7912         /** The normal browser event */
7913         browserEvent : null,
7914         /** The button pressed in a mouse event */
7915         button : -1,
7916         /** True if the shift key was down during the event */
7917         shiftKey : false,
7918         /** True if the control key was down during the event */
7919         ctrlKey : false,
7920         /** True if the alt key was down during the event */
7921         altKey : false,
7922
7923         /** Key constant 
7924         * @type Number */
7925         BACKSPACE : 8,
7926         /** Key constant 
7927         * @type Number */
7928         TAB : 9,
7929         /** Key constant 
7930         * @type Number */
7931         RETURN : 13,
7932         /** Key constant 
7933         * @type Number */
7934         ENTER : 13,
7935         /** Key constant 
7936         * @type Number */
7937         SHIFT : 16,
7938         /** Key constant 
7939         * @type Number */
7940         CONTROL : 17,
7941         /** Key constant 
7942         * @type Number */
7943         ESC : 27,
7944         /** Key constant 
7945         * @type Number */
7946         SPACE : 32,
7947         /** Key constant 
7948         * @type Number */
7949         PAGEUP : 33,
7950         /** Key constant 
7951         * @type Number */
7952         PAGEDOWN : 34,
7953         /** Key constant 
7954         * @type Number */
7955         END : 35,
7956         /** Key constant 
7957         * @type Number */
7958         HOME : 36,
7959         /** Key constant 
7960         * @type Number */
7961         LEFT : 37,
7962         /** Key constant 
7963         * @type Number */
7964         UP : 38,
7965         /** Key constant 
7966         * @type Number */
7967         RIGHT : 39,
7968         /** Key constant 
7969         * @type Number */
7970         DOWN : 40,
7971         /** Key constant 
7972         * @type Number */
7973         DELETE : 46,
7974         /** Key constant 
7975         * @type Number */
7976         F5 : 116,
7977
7978            /** @private */
7979         setEvent : function(e){
7980             if(e == this || (e && e.browserEvent)){ // already wrapped
7981                 return e;
7982             }
7983             this.browserEvent = e;
7984             if(e){
7985                 // normalize buttons
7986                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7987                 if(e.type == 'click' && this.button == -1){
7988                     this.button = 0;
7989                 }
7990                 this.type = e.type;
7991                 this.shiftKey = e.shiftKey;
7992                 // mac metaKey behaves like ctrlKey
7993                 this.ctrlKey = e.ctrlKey || e.metaKey;
7994                 this.altKey = e.altKey;
7995                 // in getKey these will be normalized for the mac
7996                 this.keyCode = e.keyCode;
7997                 // keyup warnings on firefox.
7998                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7999                 // cache the target for the delayed and or buffered events
8000                 this.target = E.getTarget(e);
8001                 // same for XY
8002                 this.xy = E.getXY(e);
8003             }else{
8004                 this.button = -1;
8005                 this.shiftKey = false;
8006                 this.ctrlKey = false;
8007                 this.altKey = false;
8008                 this.keyCode = 0;
8009                 this.charCode =0;
8010                 this.target = null;
8011                 this.xy = [0, 0];
8012             }
8013             return this;
8014         },
8015
8016         /**
8017          * Stop the event (preventDefault and stopPropagation)
8018          */
8019         stopEvent : function(){
8020             if(this.browserEvent){
8021                 if(this.browserEvent.type == 'mousedown'){
8022                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8023                 }
8024                 E.stopEvent(this.browserEvent);
8025             }
8026         },
8027
8028         /**
8029          * Prevents the browsers default handling of the event.
8030          */
8031         preventDefault : function(){
8032             if(this.browserEvent){
8033                 E.preventDefault(this.browserEvent);
8034             }
8035         },
8036
8037         /** @private */
8038         isNavKeyPress : function(){
8039             var k = this.keyCode;
8040             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8041             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8042         },
8043
8044         isSpecialKey : function(){
8045             var k = this.keyCode;
8046             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8047             (k == 16) || (k == 17) ||
8048             (k >= 18 && k <= 20) ||
8049             (k >= 33 && k <= 35) ||
8050             (k >= 36 && k <= 39) ||
8051             (k >= 44 && k <= 45);
8052         },
8053         /**
8054          * Cancels bubbling of the event.
8055          */
8056         stopPropagation : function(){
8057             if(this.browserEvent){
8058                 if(this.type == 'mousedown'){
8059                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8060                 }
8061                 E.stopPropagation(this.browserEvent);
8062             }
8063         },
8064
8065         /**
8066          * Gets the key code for the event.
8067          * @return {Number}
8068          */
8069         getCharCode : function(){
8070             return this.charCode || this.keyCode;
8071         },
8072
8073         /**
8074          * Returns a normalized keyCode for the event.
8075          * @return {Number} The key code
8076          */
8077         getKey : function(){
8078             var k = this.keyCode || this.charCode;
8079             return Roo.isSafari ? (safariKeys[k] || k) : k;
8080         },
8081
8082         /**
8083          * Gets the x coordinate of the event.
8084          * @return {Number}
8085          */
8086         getPageX : function(){
8087             return this.xy[0];
8088         },
8089
8090         /**
8091          * Gets the y coordinate of the event.
8092          * @return {Number}
8093          */
8094         getPageY : function(){
8095             return this.xy[1];
8096         },
8097
8098         /**
8099          * Gets the time of the event.
8100          * @return {Number}
8101          */
8102         getTime : function(){
8103             if(this.browserEvent){
8104                 return E.getTime(this.browserEvent);
8105             }
8106             return null;
8107         },
8108
8109         /**
8110          * Gets the page coordinates of the event.
8111          * @return {Array} The xy values like [x, y]
8112          */
8113         getXY : function(){
8114             return this.xy;
8115         },
8116
8117         /**
8118          * Gets the target for the event.
8119          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8120          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8121                 search as a number or element (defaults to 10 || document.body)
8122          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8123          * @return {HTMLelement}
8124          */
8125         getTarget : function(selector, maxDepth, returnEl){
8126             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8127         },
8128         /**
8129          * Gets the related target.
8130          * @return {HTMLElement}
8131          */
8132         getRelatedTarget : function(){
8133             if(this.browserEvent){
8134                 return E.getRelatedTarget(this.browserEvent);
8135             }
8136             return null;
8137         },
8138
8139         /**
8140          * Normalizes mouse wheel delta across browsers
8141          * @return {Number} The delta
8142          */
8143         getWheelDelta : function(){
8144             var e = this.browserEvent;
8145             var delta = 0;
8146             if(e.wheelDelta){ /* IE/Opera. */
8147                 delta = e.wheelDelta/120;
8148             }else if(e.detail){ /* Mozilla case. */
8149                 delta = -e.detail/3;
8150             }
8151             return delta;
8152         },
8153
8154         /**
8155          * Returns true if the control, meta, shift or alt key was pressed during this event.
8156          * @return {Boolean}
8157          */
8158         hasModifier : function(){
8159             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8160         },
8161
8162         /**
8163          * Returns true if the target of this event equals el or is a child of el
8164          * @param {String/HTMLElement/Element} el
8165          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8166          * @return {Boolean}
8167          */
8168         within : function(el, related){
8169             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8170             return t && Roo.fly(el).contains(t);
8171         },
8172
8173         getPoint : function(){
8174             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8175         }
8176     };
8177
8178     return new Roo.EventObjectImpl();
8179 }();
8180             
8181     /*
8182  * Based on:
8183  * Ext JS Library 1.1.1
8184  * Copyright(c) 2006-2007, Ext JS, LLC.
8185  *
8186  * Originally Released Under LGPL - original licence link has changed is not relivant.
8187  *
8188  * Fork - LGPL
8189  * <script type="text/javascript">
8190  */
8191
8192  
8193 // was in Composite Element!??!?!
8194  
8195 (function(){
8196     var D = Roo.lib.Dom;
8197     var E = Roo.lib.Event;
8198     var A = Roo.lib.Anim;
8199
8200     // local style camelizing for speed
8201     var propCache = {};
8202     var camelRe = /(-[a-z])/gi;
8203     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8204     var view = document.defaultView;
8205
8206 /**
8207  * @class Roo.Element
8208  * Represents an Element in the DOM.<br><br>
8209  * Usage:<br>
8210 <pre><code>
8211 var el = Roo.get("my-div");
8212
8213 // or with getEl
8214 var el = getEl("my-div");
8215
8216 // or with a DOM element
8217 var el = Roo.get(myDivElement);
8218 </code></pre>
8219  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8220  * each call instead of constructing a new one.<br><br>
8221  * <b>Animations</b><br />
8222  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8223  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8224 <pre>
8225 Option    Default   Description
8226 --------- --------  ---------------------------------------------
8227 duration  .35       The duration of the animation in seconds
8228 easing    easeOut   The YUI easing method
8229 callback  none      A function to execute when the anim completes
8230 scope     this      The scope (this) of the callback function
8231 </pre>
8232 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8233 * manipulate the animation. Here's an example:
8234 <pre><code>
8235 var el = Roo.get("my-div");
8236
8237 // no animation
8238 el.setWidth(100);
8239
8240 // default animation
8241 el.setWidth(100, true);
8242
8243 // animation with some options set
8244 el.setWidth(100, {
8245     duration: 1,
8246     callback: this.foo,
8247     scope: this
8248 });
8249
8250 // using the "anim" property to get the Anim object
8251 var opt = {
8252     duration: 1,
8253     callback: this.foo,
8254     scope: this
8255 };
8256 el.setWidth(100, opt);
8257 ...
8258 if(opt.anim.isAnimated()){
8259     opt.anim.stop();
8260 }
8261 </code></pre>
8262 * <b> Composite (Collections of) Elements</b><br />
8263  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8264  * @constructor Create a new Element directly.
8265  * @param {String/HTMLElement} element
8266  * @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).
8267  */
8268     Roo.Element = function(element, forceNew)
8269     {
8270         var dom = typeof element == "string" ?
8271                 document.getElementById(element) : element;
8272         
8273         this.listeners = {};
8274         
8275         if(!dom){ // invalid id/element
8276             return null;
8277         }
8278         var id = dom.id;
8279         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8280             return Roo.Element.cache[id];
8281         }
8282
8283         /**
8284          * The DOM element
8285          * @type HTMLElement
8286          */
8287         this.dom = dom;
8288
8289         /**
8290          * The DOM element ID
8291          * @type String
8292          */
8293         this.id = id || Roo.id(dom);
8294         
8295         return this; // assumed for cctor?
8296     };
8297
8298     var El = Roo.Element;
8299
8300     El.prototype = {
8301         /**
8302          * The element's default display mode  (defaults to "") 
8303          * @type String
8304          */
8305         originalDisplay : "",
8306
8307         
8308         // note this is overridden in BS version..
8309         visibilityMode : 1, 
8310         /**
8311          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8312          * @type String
8313          */
8314         defaultUnit : "px",
8315         
8316         /**
8317          * Sets the element's visibility mode. When setVisible() is called it
8318          * will use this to determine whether to set the visibility or the display property.
8319          * @param visMode Element.VISIBILITY or Element.DISPLAY
8320          * @return {Roo.Element} this
8321          */
8322         setVisibilityMode : function(visMode){
8323             this.visibilityMode = visMode;
8324             return this;
8325         },
8326         /**
8327          * Convenience method for setVisibilityMode(Element.DISPLAY)
8328          * @param {String} display (optional) What to set display to when visible
8329          * @return {Roo.Element} this
8330          */
8331         enableDisplayMode : function(display){
8332             this.setVisibilityMode(El.DISPLAY);
8333             if(typeof display != "undefined") { this.originalDisplay = display; }
8334             return this;
8335         },
8336
8337         /**
8338          * 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)
8339          * @param {String} selector The simple selector to test
8340          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8341                 search as a number or element (defaults to 10 || document.body)
8342          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8343          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8344          */
8345         findParent : function(simpleSelector, maxDepth, returnEl){
8346             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8347             maxDepth = maxDepth || 50;
8348             if(typeof maxDepth != "number"){
8349                 stopEl = Roo.getDom(maxDepth);
8350                 maxDepth = 10;
8351             }
8352             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8353                 if(dq.is(p, simpleSelector)){
8354                     return returnEl ? Roo.get(p) : p;
8355                 }
8356                 depth++;
8357                 p = p.parentNode;
8358             }
8359             return null;
8360         },
8361
8362
8363         /**
8364          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8365          * @param {String} selector The simple selector to test
8366          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8367                 search as a number or element (defaults to 10 || document.body)
8368          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8369          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8370          */
8371         findParentNode : function(simpleSelector, maxDepth, returnEl){
8372             var p = Roo.fly(this.dom.parentNode, '_internal');
8373             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8374         },
8375         
8376         /**
8377          * Looks at  the scrollable parent element
8378          */
8379         findScrollableParent : function()
8380         {
8381             var overflowRegex = /(auto|scroll)/;
8382             
8383             if(this.getStyle('position') === 'fixed'){
8384                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8385             }
8386             
8387             var excludeStaticParent = this.getStyle('position') === "absolute";
8388             
8389             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8390                 
8391                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8392                     continue;
8393                 }
8394                 
8395                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8396                     return parent;
8397                 }
8398                 
8399                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8400                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8401                 }
8402             }
8403             
8404             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8405         },
8406
8407         /**
8408          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8409          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8410          * @param {String} selector The simple selector to test
8411          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8412                 search as a number or element (defaults to 10 || document.body)
8413          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8414          */
8415         up : function(simpleSelector, maxDepth){
8416             return this.findParentNode(simpleSelector, maxDepth, true);
8417         },
8418
8419
8420
8421         /**
8422          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8423          * @param {String} selector The simple selector to test
8424          * @return {Boolean} True if this element matches the selector, else false
8425          */
8426         is : function(simpleSelector){
8427             return Roo.DomQuery.is(this.dom, simpleSelector);
8428         },
8429
8430         /**
8431          * Perform animation on this element.
8432          * @param {Object} args The YUI animation control args
8433          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8434          * @param {Function} onComplete (optional) Function to call when animation completes
8435          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8436          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8437          * @return {Roo.Element} this
8438          */
8439         animate : function(args, duration, onComplete, easing, animType){
8440             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8441             return this;
8442         },
8443
8444         /*
8445          * @private Internal animation call
8446          */
8447         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8448             animType = animType || 'run';
8449             opt = opt || {};
8450             var anim = Roo.lib.Anim[animType](
8451                 this.dom, args,
8452                 (opt.duration || defaultDur) || .35,
8453                 (opt.easing || defaultEase) || 'easeOut',
8454                 function(){
8455                     Roo.callback(cb, this);
8456                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8457                 },
8458                 this
8459             );
8460             opt.anim = anim;
8461             return anim;
8462         },
8463
8464         // private legacy anim prep
8465         preanim : function(a, i){
8466             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8467         },
8468
8469         /**
8470          * Removes worthless text nodes
8471          * @param {Boolean} forceReclean (optional) By default the element
8472          * keeps track if it has been cleaned already so
8473          * you can call this over and over. However, if you update the element and
8474          * need to force a reclean, you can pass true.
8475          */
8476         clean : function(forceReclean){
8477             if(this.isCleaned && forceReclean !== true){
8478                 return this;
8479             }
8480             var ns = /\S/;
8481             var d = this.dom, n = d.firstChild, ni = -1;
8482             while(n){
8483                 var nx = n.nextSibling;
8484                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8485                     d.removeChild(n);
8486                 }else{
8487                     n.nodeIndex = ++ni;
8488                 }
8489                 n = nx;
8490             }
8491             this.isCleaned = true;
8492             return this;
8493         },
8494
8495         // private
8496         calcOffsetsTo : function(el){
8497             el = Roo.get(el);
8498             var d = el.dom;
8499             var restorePos = false;
8500             if(el.getStyle('position') == 'static'){
8501                 el.position('relative');
8502                 restorePos = true;
8503             }
8504             var x = 0, y =0;
8505             var op = this.dom;
8506             while(op && op != d && op.tagName != 'HTML'){
8507                 x+= op.offsetLeft;
8508                 y+= op.offsetTop;
8509                 op = op.offsetParent;
8510             }
8511             if(restorePos){
8512                 el.position('static');
8513             }
8514             return [x, y];
8515         },
8516
8517         /**
8518          * Scrolls this element into view within the passed container.
8519          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8520          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8521          * @return {Roo.Element} this
8522          */
8523         scrollIntoView : function(container, hscroll){
8524             var c = Roo.getDom(container) || document.body;
8525             var el = this.dom;
8526
8527             var o = this.calcOffsetsTo(c),
8528                 l = o[0],
8529                 t = o[1],
8530                 b = t+el.offsetHeight,
8531                 r = l+el.offsetWidth;
8532
8533             var ch = c.clientHeight;
8534             var ct = parseInt(c.scrollTop, 10);
8535             var cl = parseInt(c.scrollLeft, 10);
8536             var cb = ct + ch;
8537             var cr = cl + c.clientWidth;
8538
8539             if(t < ct){
8540                 c.scrollTop = t;
8541             }else if(b > cb){
8542                 c.scrollTop = b-ch;
8543             }
8544
8545             if(hscroll !== false){
8546                 if(l < cl){
8547                     c.scrollLeft = l;
8548                 }else if(r > cr){
8549                     c.scrollLeft = r-c.clientWidth;
8550                 }
8551             }
8552             return this;
8553         },
8554
8555         // private
8556         scrollChildIntoView : function(child, hscroll){
8557             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8558         },
8559
8560         /**
8561          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8562          * the new height may not be available immediately.
8563          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8564          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8565          * @param {Function} onComplete (optional) Function to call when animation completes
8566          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8567          * @return {Roo.Element} this
8568          */
8569         autoHeight : function(animate, duration, onComplete, easing){
8570             var oldHeight = this.getHeight();
8571             this.clip();
8572             this.setHeight(1); // force clipping
8573             setTimeout(function(){
8574                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8575                 if(!animate){
8576                     this.setHeight(height);
8577                     this.unclip();
8578                     if(typeof onComplete == "function"){
8579                         onComplete();
8580                     }
8581                 }else{
8582                     this.setHeight(oldHeight); // restore original height
8583                     this.setHeight(height, animate, duration, function(){
8584                         this.unclip();
8585                         if(typeof onComplete == "function") { onComplete(); }
8586                     }.createDelegate(this), easing);
8587                 }
8588             }.createDelegate(this), 0);
8589             return this;
8590         },
8591
8592         /**
8593          * Returns true if this element is an ancestor of the passed element
8594          * @param {HTMLElement/String} el The element to check
8595          * @return {Boolean} True if this element is an ancestor of el, else false
8596          */
8597         contains : function(el){
8598             if(!el){return false;}
8599             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8600         },
8601
8602         /**
8603          * Checks whether the element is currently visible using both visibility and display properties.
8604          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8605          * @return {Boolean} True if the element is currently visible, else false
8606          */
8607         isVisible : function(deep) {
8608             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8609             if(deep !== true || !vis){
8610                 return vis;
8611             }
8612             var p = this.dom.parentNode;
8613             while(p && p.tagName.toLowerCase() != "body"){
8614                 if(!Roo.fly(p, '_isVisible').isVisible()){
8615                     return false;
8616                 }
8617                 p = p.parentNode;
8618             }
8619             return true;
8620         },
8621
8622         /**
8623          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8624          * @param {String} selector The CSS selector
8625          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8626          * @return {CompositeElement/CompositeElementLite} The composite element
8627          */
8628         select : function(selector, unique){
8629             return El.select(selector, unique, this.dom);
8630         },
8631
8632         /**
8633          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8634          * @param {String} selector The CSS selector
8635          * @return {Array} An array of the matched nodes
8636          */
8637         query : function(selector, unique){
8638             return Roo.DomQuery.select(selector, this.dom);
8639         },
8640
8641         /**
8642          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8643          * @param {String} selector The CSS selector
8644          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8645          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8646          */
8647         child : function(selector, returnDom){
8648             var n = Roo.DomQuery.selectNode(selector, this.dom);
8649             return returnDom ? n : Roo.get(n);
8650         },
8651
8652         /**
8653          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8654          * @param {String} selector The CSS selector
8655          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8656          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8657          */
8658         down : function(selector, returnDom){
8659             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8660             return returnDom ? n : Roo.get(n);
8661         },
8662
8663         /**
8664          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8665          * @param {String} group The group the DD object is member of
8666          * @param {Object} config The DD config object
8667          * @param {Object} overrides An object containing methods to override/implement on the DD object
8668          * @return {Roo.dd.DD} The DD object
8669          */
8670         initDD : function(group, config, overrides){
8671             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8672             return Roo.apply(dd, overrides);
8673         },
8674
8675         /**
8676          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8677          * @param {String} group The group the DDProxy object is member of
8678          * @param {Object} config The DDProxy config object
8679          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8680          * @return {Roo.dd.DDProxy} The DDProxy object
8681          */
8682         initDDProxy : function(group, config, overrides){
8683             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8684             return Roo.apply(dd, overrides);
8685         },
8686
8687         /**
8688          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8689          * @param {String} group The group the DDTarget object is member of
8690          * @param {Object} config The DDTarget config object
8691          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8692          * @return {Roo.dd.DDTarget} The DDTarget object
8693          */
8694         initDDTarget : function(group, config, overrides){
8695             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8696             return Roo.apply(dd, overrides);
8697         },
8698
8699         /**
8700          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8701          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8702          * @param {Boolean} visible Whether the element is visible
8703          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8704          * @return {Roo.Element} this
8705          */
8706          setVisible : function(visible, animate){
8707             if(!animate || !A){
8708                 if(this.visibilityMode == El.DISPLAY){
8709                     this.setDisplayed(visible);
8710                 }else{
8711                     this.fixDisplay();
8712                     this.dom.style.visibility = visible ? "visible" : "hidden";
8713                 }
8714             }else{
8715                 // closure for composites
8716                 var dom = this.dom;
8717                 var visMode = this.visibilityMode;
8718                 if(visible){
8719                     this.setOpacity(.01);
8720                     this.setVisible(true);
8721                 }
8722                 this.anim({opacity: { to: (visible?1:0) }},
8723                       this.preanim(arguments, 1),
8724                       null, .35, 'easeIn', function(){
8725                          if(!visible){
8726                              if(visMode == El.DISPLAY){
8727                                  dom.style.display = "none";
8728                              }else{
8729                                  dom.style.visibility = "hidden";
8730                              }
8731                              Roo.get(dom).setOpacity(1);
8732                          }
8733                      });
8734             }
8735             return this;
8736         },
8737
8738         /**
8739          * Returns true if display is not "none"
8740          * @return {Boolean}
8741          */
8742         isDisplayed : function() {
8743             return this.getStyle("display") != "none";
8744         },
8745
8746         /**
8747          * Toggles the element's visibility or display, depending on visibility mode.
8748          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8749          * @return {Roo.Element} this
8750          */
8751         toggle : function(animate){
8752             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8753             return this;
8754         },
8755
8756         /**
8757          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8758          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8759          * @return {Roo.Element} this
8760          */
8761         setDisplayed : function(value) {
8762             if(typeof value == "boolean"){
8763                value = value ? this.originalDisplay : "none";
8764             }
8765             this.setStyle("display", value);
8766             return this;
8767         },
8768
8769         /**
8770          * Tries to focus the element. Any exceptions are caught and ignored.
8771          * @return {Roo.Element} this
8772          */
8773         focus : function() {
8774             try{
8775                 this.dom.focus();
8776             }catch(e){}
8777             return this;
8778         },
8779
8780         /**
8781          * Tries to blur the element. Any exceptions are caught and ignored.
8782          * @return {Roo.Element} this
8783          */
8784         blur : function() {
8785             try{
8786                 this.dom.blur();
8787             }catch(e){}
8788             return this;
8789         },
8790
8791         /**
8792          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8793          * @param {String/Array} className The CSS class to add, or an array of classes
8794          * @return {Roo.Element} this
8795          */
8796         addClass : function(className){
8797             if(className instanceof Array){
8798                 for(var i = 0, len = className.length; i < len; i++) {
8799                     this.addClass(className[i]);
8800                 }
8801             }else{
8802                 if(className && !this.hasClass(className)){
8803                     if (this.dom instanceof SVGElement) {
8804                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8805                     } else {
8806                         this.dom.className = this.dom.className + " " + className;
8807                     }
8808                 }
8809             }
8810             return this;
8811         },
8812
8813         /**
8814          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8815          * @param {String/Array} className The CSS class to add, or an array of classes
8816          * @return {Roo.Element} this
8817          */
8818         radioClass : function(className){
8819             var siblings = this.dom.parentNode.childNodes;
8820             for(var i = 0; i < siblings.length; i++) {
8821                 var s = siblings[i];
8822                 if(s.nodeType == 1){
8823                     Roo.get(s).removeClass(className);
8824                 }
8825             }
8826             this.addClass(className);
8827             return this;
8828         },
8829
8830         /**
8831          * Removes one or more CSS classes from the element.
8832          * @param {String/Array} className The CSS class to remove, or an array of classes
8833          * @return {Roo.Element} this
8834          */
8835         removeClass : function(className){
8836             
8837             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8838             if(!className || !cn){
8839                 return this;
8840             }
8841             if(className instanceof Array){
8842                 for(var i = 0, len = className.length; i < len; i++) {
8843                     this.removeClass(className[i]);
8844                 }
8845             }else{
8846                 if(this.hasClass(className)){
8847                     var re = this.classReCache[className];
8848                     if (!re) {
8849                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8850                        this.classReCache[className] = re;
8851                     }
8852                     if (this.dom instanceof SVGElement) {
8853                         this.dom.className.baseVal = cn.replace(re, " ");
8854                     } else {
8855                         this.dom.className = cn.replace(re, " ");
8856                     }
8857                 }
8858             }
8859             return this;
8860         },
8861
8862         // private
8863         classReCache: {},
8864
8865         /**
8866          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8867          * @param {String} className The CSS class to toggle
8868          * @return {Roo.Element} this
8869          */
8870         toggleClass : function(className){
8871             if(this.hasClass(className)){
8872                 this.removeClass(className);
8873             }else{
8874                 this.addClass(className);
8875             }
8876             return this;
8877         },
8878
8879         /**
8880          * Checks if the specified CSS class exists on this element's DOM node.
8881          * @param {String} className The CSS class to check for
8882          * @return {Boolean} True if the class exists, else false
8883          */
8884         hasClass : function(className){
8885             if (this.dom instanceof SVGElement) {
8886                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8887             } 
8888             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8889         },
8890
8891         /**
8892          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8893          * @param {String} oldClassName The CSS class to replace
8894          * @param {String} newClassName The replacement CSS class
8895          * @return {Roo.Element} this
8896          */
8897         replaceClass : function(oldClassName, newClassName){
8898             this.removeClass(oldClassName);
8899             this.addClass(newClassName);
8900             return this;
8901         },
8902
8903         /**
8904          * Returns an object with properties matching the styles requested.
8905          * For example, el.getStyles('color', 'font-size', 'width') might return
8906          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8907          * @param {String} style1 A style name
8908          * @param {String} style2 A style name
8909          * @param {String} etc.
8910          * @return {Object} The style object
8911          */
8912         getStyles : function(){
8913             var a = arguments, len = a.length, r = {};
8914             for(var i = 0; i < len; i++){
8915                 r[a[i]] = this.getStyle(a[i]);
8916             }
8917             return r;
8918         },
8919
8920         /**
8921          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8922          * @param {String} property The style property whose value is returned.
8923          * @return {String} The current value of the style property for this element.
8924          */
8925         getStyle : function(){
8926             return view && view.getComputedStyle ?
8927                 function(prop){
8928                     var el = this.dom, v, cs, camel;
8929                     if(prop == 'float'){
8930                         prop = "cssFloat";
8931                     }
8932                     if(el.style && (v = el.style[prop])){
8933                         return v;
8934                     }
8935                     if(cs = view.getComputedStyle(el, "")){
8936                         if(!(camel = propCache[prop])){
8937                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8938                         }
8939                         return cs[camel];
8940                     }
8941                     return null;
8942                 } :
8943                 function(prop){
8944                     var el = this.dom, v, cs, camel;
8945                     if(prop == 'opacity'){
8946                         if(typeof el.style.filter == 'string'){
8947                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8948                             if(m){
8949                                 var fv = parseFloat(m[1]);
8950                                 if(!isNaN(fv)){
8951                                     return fv ? fv / 100 : 0;
8952                                 }
8953                             }
8954                         }
8955                         return 1;
8956                     }else if(prop == 'float'){
8957                         prop = "styleFloat";
8958                     }
8959                     if(!(camel = propCache[prop])){
8960                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8961                     }
8962                     if(v = el.style[camel]){
8963                         return v;
8964                     }
8965                     if(cs = el.currentStyle){
8966                         return cs[camel];
8967                     }
8968                     return null;
8969                 };
8970         }(),
8971
8972         /**
8973          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8974          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8975          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8976          * @return {Roo.Element} this
8977          */
8978         setStyle : function(prop, value){
8979             if(typeof prop == "string"){
8980                 
8981                 if (prop == 'float') {
8982                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8983                     return this;
8984                 }
8985                 
8986                 var camel;
8987                 if(!(camel = propCache[prop])){
8988                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8989                 }
8990                 
8991                 if(camel == 'opacity') {
8992                     this.setOpacity(value);
8993                 }else{
8994                     this.dom.style[camel] = value;
8995                 }
8996             }else{
8997                 for(var style in prop){
8998                     if(typeof prop[style] != "function"){
8999                        this.setStyle(style, prop[style]);
9000                     }
9001                 }
9002             }
9003             return this;
9004         },
9005
9006         /**
9007          * More flexible version of {@link #setStyle} for setting style properties.
9008          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9009          * a function which returns such a specification.
9010          * @return {Roo.Element} this
9011          */
9012         applyStyles : function(style){
9013             Roo.DomHelper.applyStyles(this.dom, style);
9014             return this;
9015         },
9016
9017         /**
9018           * 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).
9019           * @return {Number} The X position of the element
9020           */
9021         getX : function(){
9022             return D.getX(this.dom);
9023         },
9024
9025         /**
9026           * 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).
9027           * @return {Number} The Y position of the element
9028           */
9029         getY : function(){
9030             return D.getY(this.dom);
9031         },
9032
9033         /**
9034           * 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).
9035           * @return {Array} The XY position of the element
9036           */
9037         getXY : function(){
9038             return D.getXY(this.dom);
9039         },
9040
9041         /**
9042          * 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).
9043          * @param {Number} The X position of the element
9044          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9045          * @return {Roo.Element} this
9046          */
9047         setX : function(x, animate){
9048             if(!animate || !A){
9049                 D.setX(this.dom, x);
9050             }else{
9051                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9052             }
9053             return this;
9054         },
9055
9056         /**
9057          * 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).
9058          * @param {Number} The Y position of the element
9059          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9060          * @return {Roo.Element} this
9061          */
9062         setY : function(y, animate){
9063             if(!animate || !A){
9064                 D.setY(this.dom, y);
9065             }else{
9066                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9067             }
9068             return this;
9069         },
9070
9071         /**
9072          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9073          * @param {String} left The left CSS property value
9074          * @return {Roo.Element} this
9075          */
9076         setLeft : function(left){
9077             this.setStyle("left", this.addUnits(left));
9078             return this;
9079         },
9080
9081         /**
9082          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9083          * @param {String} top The top CSS property value
9084          * @return {Roo.Element} this
9085          */
9086         setTop : function(top){
9087             this.setStyle("top", this.addUnits(top));
9088             return this;
9089         },
9090
9091         /**
9092          * Sets the element's CSS right style.
9093          * @param {String} right The right CSS property value
9094          * @return {Roo.Element} this
9095          */
9096         setRight : function(right){
9097             this.setStyle("right", this.addUnits(right));
9098             return this;
9099         },
9100
9101         /**
9102          * Sets the element's CSS bottom style.
9103          * @param {String} bottom The bottom CSS property value
9104          * @return {Roo.Element} this
9105          */
9106         setBottom : function(bottom){
9107             this.setStyle("bottom", this.addUnits(bottom));
9108             return this;
9109         },
9110
9111         /**
9112          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9113          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9114          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9115          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9116          * @return {Roo.Element} this
9117          */
9118         setXY : function(pos, animate){
9119             if(!animate || !A){
9120                 D.setXY(this.dom, pos);
9121             }else{
9122                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9123             }
9124             return this;
9125         },
9126
9127         /**
9128          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9129          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9130          * @param {Number} x X value for new position (coordinates are page-based)
9131          * @param {Number} y Y value for new position (coordinates are page-based)
9132          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9133          * @return {Roo.Element} this
9134          */
9135         setLocation : function(x, y, animate){
9136             this.setXY([x, y], this.preanim(arguments, 2));
9137             return this;
9138         },
9139
9140         /**
9141          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9142          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9143          * @param {Number} x X value for new position (coordinates are page-based)
9144          * @param {Number} y Y value for new position (coordinates are page-based)
9145          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9146          * @return {Roo.Element} this
9147          */
9148         moveTo : function(x, y, animate){
9149             this.setXY([x, y], this.preanim(arguments, 2));
9150             return this;
9151         },
9152
9153         /**
9154          * Returns the region of the given element.
9155          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9156          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9157          */
9158         getRegion : function(){
9159             return D.getRegion(this.dom);
9160         },
9161
9162         /**
9163          * Returns the offset height of the element
9164          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9165          * @return {Number} The element's height
9166          */
9167         getHeight : function(contentHeight){
9168             var h = this.dom.offsetHeight || 0;
9169             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9170         },
9171
9172         /**
9173          * Returns the offset width of the element
9174          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9175          * @return {Number} The element's width
9176          */
9177         getWidth : function(contentWidth){
9178             var w = this.dom.offsetWidth || 0;
9179             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9180         },
9181
9182         /**
9183          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9184          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9185          * if a height has not been set using CSS.
9186          * @return {Number}
9187          */
9188         getComputedHeight : function(){
9189             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9190             if(!h){
9191                 h = parseInt(this.getStyle('height'), 10) || 0;
9192                 if(!this.isBorderBox()){
9193                     h += this.getFrameWidth('tb');
9194                 }
9195             }
9196             return h;
9197         },
9198
9199         /**
9200          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9201          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9202          * if a width has not been set using CSS.
9203          * @return {Number}
9204          */
9205         getComputedWidth : function(){
9206             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9207             if(!w){
9208                 w = parseInt(this.getStyle('width'), 10) || 0;
9209                 if(!this.isBorderBox()){
9210                     w += this.getFrameWidth('lr');
9211                 }
9212             }
9213             return w;
9214         },
9215
9216         /**
9217          * Returns the size of the element.
9218          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9219          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9220          */
9221         getSize : function(contentSize){
9222             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9223         },
9224
9225         /**
9226          * Returns the width and height of the viewport.
9227          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9228          */
9229         getViewSize : function(){
9230             var d = this.dom, doc = document, aw = 0, ah = 0;
9231             if(d == doc || d == doc.body){
9232                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9233             }else{
9234                 return {
9235                     width : d.clientWidth,
9236                     height: d.clientHeight
9237                 };
9238             }
9239         },
9240
9241         /**
9242          * Returns the value of the "value" attribute
9243          * @param {Boolean} asNumber true to parse the value as a number
9244          * @return {String/Number}
9245          */
9246         getValue : function(asNumber){
9247             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9248         },
9249
9250         // private
9251         adjustWidth : function(width){
9252             if(typeof width == "number"){
9253                 if(this.autoBoxAdjust && !this.isBorderBox()){
9254                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9255                 }
9256                 if(width < 0){
9257                     width = 0;
9258                 }
9259             }
9260             return width;
9261         },
9262
9263         // private
9264         adjustHeight : function(height){
9265             if(typeof height == "number"){
9266                if(this.autoBoxAdjust && !this.isBorderBox()){
9267                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9268                }
9269                if(height < 0){
9270                    height = 0;
9271                }
9272             }
9273             return height;
9274         },
9275
9276         /**
9277          * Set the width of the element
9278          * @param {Number} width The new width
9279          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9280          * @return {Roo.Element} this
9281          */
9282         setWidth : function(width, animate){
9283             width = this.adjustWidth(width);
9284             if(!animate || !A){
9285                 this.dom.style.width = this.addUnits(width);
9286             }else{
9287                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9288             }
9289             return this;
9290         },
9291
9292         /**
9293          * Set the height of the element
9294          * @param {Number} height The new height
9295          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9296          * @return {Roo.Element} this
9297          */
9298          setHeight : function(height, animate){
9299             height = this.adjustHeight(height);
9300             if(!animate || !A){
9301                 this.dom.style.height = this.addUnits(height);
9302             }else{
9303                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9310          * @param {Number} width The new width
9311          * @param {Number} height The new height
9312          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9313          * @return {Roo.Element} this
9314          */
9315          setSize : function(width, height, animate){
9316             if(typeof width == "object"){ // in case of object from getSize()
9317                 height = width.height; width = width.width;
9318             }
9319             width = this.adjustWidth(width); height = this.adjustHeight(height);
9320             if(!animate || !A){
9321                 this.dom.style.width = this.addUnits(width);
9322                 this.dom.style.height = this.addUnits(height);
9323             }else{
9324                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9325             }
9326             return this;
9327         },
9328
9329         /**
9330          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9331          * @param {Number} x X value for new position (coordinates are page-based)
9332          * @param {Number} y Y value for new position (coordinates are page-based)
9333          * @param {Number} width The new width
9334          * @param {Number} height The new height
9335          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9336          * @return {Roo.Element} this
9337          */
9338         setBounds : function(x, y, width, height, animate){
9339             if(!animate || !A){
9340                 this.setSize(width, height);
9341                 this.setLocation(x, y);
9342             }else{
9343                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9344                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9345                               this.preanim(arguments, 4), 'motion');
9346             }
9347             return this;
9348         },
9349
9350         /**
9351          * 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.
9352          * @param {Roo.lib.Region} region The region to fill
9353          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9354          * @return {Roo.Element} this
9355          */
9356         setRegion : function(region, animate){
9357             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9358             return this;
9359         },
9360
9361         /**
9362          * Appends an event handler
9363          *
9364          * @param {String}   eventName     The type of event to append
9365          * @param {Function} fn        The method the event invokes
9366          * @param {Object} scope       (optional) The scope (this object) of the fn
9367          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9368          */
9369         addListener : function(eventName, fn, scope, options)
9370         {
9371             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9372                 this.addListener('touchstart', this.onTapHandler, this);
9373             }
9374             
9375             // we need to handle a special case where dom element is a svg element.
9376             // in this case we do not actua
9377             if (!this.dom) {
9378                 return;
9379             }
9380             
9381             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9382                 if (typeof(this.listeners[eventName]) == 'undefined') {
9383                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9384                 }
9385                 this.listeners[eventName].addListener(fn, scope, options);
9386                 return;
9387             }
9388             
9389                 
9390             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9391             
9392             
9393         },
9394         tapedTwice : false,
9395         onTapHandler : function(event)
9396         {
9397             if(!this.tapedTwice) {
9398                 this.tapedTwice = true;
9399                 var s = this;
9400                 setTimeout( function() {
9401                     s.tapedTwice = false;
9402                 }, 300 );
9403                 return;
9404             }
9405             event.preventDefault();
9406             var revent = new MouseEvent('dblclick',  {
9407                 view: window,
9408                 bubbles: true,
9409                 cancelable: true
9410             });
9411              
9412             this.dom.dispatchEvent(revent);
9413             //action on double tap goes below
9414              
9415         }, 
9416  
9417         /**
9418          * Removes an event handler from this element
9419          * @param {String} eventName the type of event to remove
9420          * @param {Function} fn the method the event invokes
9421          * @param {Function} scope (needed for svg fake listeners)
9422          * @return {Roo.Element} this
9423          */
9424         removeListener : function(eventName, fn, scope){
9425             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9426             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9427                 return this;
9428             }
9429             this.listeners[eventName].removeListener(fn, scope);
9430             return this;
9431         },
9432
9433         /**
9434          * Removes all previous added listeners from this element
9435          * @return {Roo.Element} this
9436          */
9437         removeAllListeners : function(){
9438             E.purgeElement(this.dom);
9439             this.listeners = {};
9440             return this;
9441         },
9442
9443         relayEvent : function(eventName, observable){
9444             this.on(eventName, function(e){
9445                 observable.fireEvent(eventName, e);
9446             });
9447         },
9448
9449         
9450         /**
9451          * Set the opacity of the element
9452          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454          * @return {Roo.Element} this
9455          */
9456          setOpacity : function(opacity, animate){
9457             if(!animate || !A){
9458                 var s = this.dom.style;
9459                 if(Roo.isIE){
9460                     s.zoom = 1;
9461                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9462                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9463                 }else{
9464                     s.opacity = opacity;
9465                 }
9466             }else{
9467                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9468             }
9469             return this;
9470         },
9471
9472         /**
9473          * Gets the left X coordinate
9474          * @param {Boolean} local True to get the local css position instead of page coordinate
9475          * @return {Number}
9476          */
9477         getLeft : function(local){
9478             if(!local){
9479                 return this.getX();
9480             }else{
9481                 return parseInt(this.getStyle("left"), 10) || 0;
9482             }
9483         },
9484
9485         /**
9486          * Gets the right X coordinate of the element (element X position + element width)
9487          * @param {Boolean} local True to get the local css position instead of page coordinate
9488          * @return {Number}
9489          */
9490         getRight : function(local){
9491             if(!local){
9492                 return this.getX() + this.getWidth();
9493             }else{
9494                 return (this.getLeft(true) + this.getWidth()) || 0;
9495             }
9496         },
9497
9498         /**
9499          * Gets the top Y coordinate
9500          * @param {Boolean} local True to get the local css position instead of page coordinate
9501          * @return {Number}
9502          */
9503         getTop : function(local) {
9504             if(!local){
9505                 return this.getY();
9506             }else{
9507                 return parseInt(this.getStyle("top"), 10) || 0;
9508             }
9509         },
9510
9511         /**
9512          * Gets the bottom Y coordinate of the element (element Y position + element height)
9513          * @param {Boolean} local True to get the local css position instead of page coordinate
9514          * @return {Number}
9515          */
9516         getBottom : function(local){
9517             if(!local){
9518                 return this.getY() + this.getHeight();
9519             }else{
9520                 return (this.getTop(true) + this.getHeight()) || 0;
9521             }
9522         },
9523
9524         /**
9525         * Initializes positioning on this element. If a desired position is not passed, it will make the
9526         * the element positioned relative IF it is not already positioned.
9527         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9528         * @param {Number} zIndex (optional) The zIndex to apply
9529         * @param {Number} x (optional) Set the page X position
9530         * @param {Number} y (optional) Set the page Y position
9531         */
9532         position : function(pos, zIndex, x, y){
9533             if(!pos){
9534                if(this.getStyle('position') == 'static'){
9535                    this.setStyle('position', 'relative');
9536                }
9537             }else{
9538                 this.setStyle("position", pos);
9539             }
9540             if(zIndex){
9541                 this.setStyle("z-index", zIndex);
9542             }
9543             if(x !== undefined && y !== undefined){
9544                 this.setXY([x, y]);
9545             }else if(x !== undefined){
9546                 this.setX(x);
9547             }else if(y !== undefined){
9548                 this.setY(y);
9549             }
9550         },
9551
9552         /**
9553         * Clear positioning back to the default when the document was loaded
9554         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9555         * @return {Roo.Element} this
9556          */
9557         clearPositioning : function(value){
9558             value = value ||'';
9559             this.setStyle({
9560                 "left": value,
9561                 "right": value,
9562                 "top": value,
9563                 "bottom": value,
9564                 "z-index": "",
9565                 "position" : "static"
9566             });
9567             return this;
9568         },
9569
9570         /**
9571         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9572         * snapshot before performing an update and then restoring the element.
9573         * @return {Object}
9574         */
9575         getPositioning : function(){
9576             var l = this.getStyle("left");
9577             var t = this.getStyle("top");
9578             return {
9579                 "position" : this.getStyle("position"),
9580                 "left" : l,
9581                 "right" : l ? "" : this.getStyle("right"),
9582                 "top" : t,
9583                 "bottom" : t ? "" : this.getStyle("bottom"),
9584                 "z-index" : this.getStyle("z-index")
9585             };
9586         },
9587
9588         /**
9589          * Gets the width of the border(s) for the specified side(s)
9590          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9591          * passing lr would get the border (l)eft width + the border (r)ight width.
9592          * @return {Number} The width of the sides passed added together
9593          */
9594         getBorderWidth : function(side){
9595             return this.addStyles(side, El.borders);
9596         },
9597
9598         /**
9599          * Gets the width of the padding(s) for the specified side(s)
9600          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9601          * passing lr would get the padding (l)eft + the padding (r)ight.
9602          * @return {Number} The padding of the sides passed added together
9603          */
9604         getPadding : function(side){
9605             return this.addStyles(side, El.paddings);
9606         },
9607
9608         /**
9609         * Set positioning with an object returned by getPositioning().
9610         * @param {Object} posCfg
9611         * @return {Roo.Element} this
9612          */
9613         setPositioning : function(pc){
9614             this.applyStyles(pc);
9615             if(pc.right == "auto"){
9616                 this.dom.style.right = "";
9617             }
9618             if(pc.bottom == "auto"){
9619                 this.dom.style.bottom = "";
9620             }
9621             return this;
9622         },
9623
9624         // private
9625         fixDisplay : function(){
9626             if(this.getStyle("display") == "none"){
9627                 this.setStyle("visibility", "hidden");
9628                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9629                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9630                     this.setStyle("display", "block");
9631                 }
9632             }
9633         },
9634
9635         /**
9636          * Quick set left and top adding default units
9637          * @param {String} left The left CSS property value
9638          * @param {String} top The top CSS property value
9639          * @return {Roo.Element} this
9640          */
9641          setLeftTop : function(left, top){
9642             this.dom.style.left = this.addUnits(left);
9643             this.dom.style.top = this.addUnits(top);
9644             return this;
9645         },
9646
9647         /**
9648          * Move this element relative to its current position.
9649          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9650          * @param {Number} distance How far to move the element in pixels
9651          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652          * @return {Roo.Element} this
9653          */
9654          move : function(direction, distance, animate){
9655             var xy = this.getXY();
9656             direction = direction.toLowerCase();
9657             switch(direction){
9658                 case "l":
9659                 case "left":
9660                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9661                     break;
9662                case "r":
9663                case "right":
9664                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9665                     break;
9666                case "t":
9667                case "top":
9668                case "up":
9669                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9670                     break;
9671                case "b":
9672                case "bottom":
9673                case "down":
9674                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9675                     break;
9676             }
9677             return this;
9678         },
9679
9680         /**
9681          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9682          * @return {Roo.Element} this
9683          */
9684         clip : function(){
9685             if(!this.isClipped){
9686                this.isClipped = true;
9687                this.originalClip = {
9688                    "o": this.getStyle("overflow"),
9689                    "x": this.getStyle("overflow-x"),
9690                    "y": this.getStyle("overflow-y")
9691                };
9692                this.setStyle("overflow", "hidden");
9693                this.setStyle("overflow-x", "hidden");
9694                this.setStyle("overflow-y", "hidden");
9695             }
9696             return this;
9697         },
9698
9699         /**
9700          *  Return clipping (overflow) to original clipping before clip() was called
9701          * @return {Roo.Element} this
9702          */
9703         unclip : function(){
9704             if(this.isClipped){
9705                 this.isClipped = false;
9706                 var o = this.originalClip;
9707                 if(o.o){this.setStyle("overflow", o.o);}
9708                 if(o.x){this.setStyle("overflow-x", o.x);}
9709                 if(o.y){this.setStyle("overflow-y", o.y);}
9710             }
9711             return this;
9712         },
9713
9714
9715         /**
9716          * Gets the x,y coordinates specified by the anchor position on the element.
9717          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9718          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9719          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9720          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9721          * @return {Array} [x, y] An array containing the element's x and y coordinates
9722          */
9723         getAnchorXY : function(anchor, local, s){
9724             //Passing a different size is useful for pre-calculating anchors,
9725             //especially for anchored animations that change the el size.
9726
9727             var w, h, vp = false;
9728             if(!s){
9729                 var d = this.dom;
9730                 if(d == document.body || d == document){
9731                     vp = true;
9732                     w = D.getViewWidth(); h = D.getViewHeight();
9733                 }else{
9734                     w = this.getWidth(); h = this.getHeight();
9735                 }
9736             }else{
9737                 w = s.width;  h = s.height;
9738             }
9739             var x = 0, y = 0, r = Math.round;
9740             switch((anchor || "tl").toLowerCase()){
9741                 case "c":
9742                     x = r(w*.5);
9743                     y = r(h*.5);
9744                 break;
9745                 case "t":
9746                     x = r(w*.5);
9747                     y = 0;
9748                 break;
9749                 case "l":
9750                     x = 0;
9751                     y = r(h*.5);
9752                 break;
9753                 case "r":
9754                     x = w;
9755                     y = r(h*.5);
9756                 break;
9757                 case "b":
9758                     x = r(w*.5);
9759                     y = h;
9760                 break;
9761                 case "tl":
9762                     x = 0;
9763                     y = 0;
9764                 break;
9765                 case "bl":
9766                     x = 0;
9767                     y = h;
9768                 break;
9769                 case "br":
9770                     x = w;
9771                     y = h;
9772                 break;
9773                 case "tr":
9774                     x = w;
9775                     y = 0;
9776                 break;
9777             }
9778             if(local === true){
9779                 return [x, y];
9780             }
9781             if(vp){
9782                 var sc = this.getScroll();
9783                 return [x + sc.left, y + sc.top];
9784             }
9785             //Add the element's offset xy
9786             var o = this.getXY();
9787             return [x+o[0], y+o[1]];
9788         },
9789
9790         /**
9791          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9792          * supported position values.
9793          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9794          * @param {String} position The position to align to.
9795          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9796          * @return {Array} [x, y]
9797          */
9798         getAlignToXY : function(el, p, o)
9799         {
9800             el = Roo.get(el);
9801             var d = this.dom;
9802             if(!el.dom){
9803                 throw "Element.alignTo with an element that doesn't exist";
9804             }
9805             var c = false; //constrain to viewport
9806             var p1 = "", p2 = "";
9807             o = o || [0,0];
9808
9809             if(!p){
9810                 p = "tl-bl";
9811             }else if(p == "?"){
9812                 p = "tl-bl?";
9813             }else if(p.indexOf("-") == -1){
9814                 p = "tl-" + p;
9815             }
9816             p = p.toLowerCase();
9817             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9818             if(!m){
9819                throw "Element.alignTo with an invalid alignment " + p;
9820             }
9821             p1 = m[1]; p2 = m[2]; c = !!m[3];
9822
9823             //Subtract the aligned el's internal xy from the target's offset xy
9824             //plus custom offset to get the aligned el's new offset xy
9825             var a1 = this.getAnchorXY(p1, true);
9826             var a2 = el.getAnchorXY(p2, false);
9827             var x = a2[0] - a1[0] + o[0];
9828             var y = a2[1] - a1[1] + o[1];
9829             if(c){
9830                 //constrain the aligned el to viewport if necessary
9831                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9832                 // 5px of margin for ie
9833                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9834
9835                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9836                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9837                 //otherwise swap the aligned el to the opposite border of the target.
9838                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9839                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9840                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9841                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9842
9843                var doc = document;
9844                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9845                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9846
9847                if((x+w) > dw + scrollX){
9848                     x = swapX ? r.left-w : dw+scrollX-w;
9849                 }
9850                if(x < scrollX){
9851                    x = swapX ? r.right : scrollX;
9852                }
9853                if((y+h) > dh + scrollY){
9854                     y = swapY ? r.top-h : dh+scrollY-h;
9855                 }
9856                if (y < scrollY){
9857                    y = swapY ? r.bottom : scrollY;
9858                }
9859             }
9860             return [x,y];
9861         },
9862
9863         // private
9864         getConstrainToXY : function(){
9865             var os = {top:0, left:0, bottom:0, right: 0};
9866
9867             return function(el, local, offsets, proposedXY){
9868                 el = Roo.get(el);
9869                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9870
9871                 var vw, vh, vx = 0, vy = 0;
9872                 if(el.dom == document.body || el.dom == document){
9873                     vw = Roo.lib.Dom.getViewWidth();
9874                     vh = Roo.lib.Dom.getViewHeight();
9875                 }else{
9876                     vw = el.dom.clientWidth;
9877                     vh = el.dom.clientHeight;
9878                     if(!local){
9879                         var vxy = el.getXY();
9880                         vx = vxy[0];
9881                         vy = vxy[1];
9882                     }
9883                 }
9884
9885                 var s = el.getScroll();
9886
9887                 vx += offsets.left + s.left;
9888                 vy += offsets.top + s.top;
9889
9890                 vw -= offsets.right;
9891                 vh -= offsets.bottom;
9892
9893                 var vr = vx+vw;
9894                 var vb = vy+vh;
9895
9896                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9897                 var x = xy[0], y = xy[1];
9898                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9899
9900                 // only move it if it needs it
9901                 var moved = false;
9902
9903                 // first validate right/bottom
9904                 if((x + w) > vr){
9905                     x = vr - w;
9906                     moved = true;
9907                 }
9908                 if((y + h) > vb){
9909                     y = vb - h;
9910                     moved = true;
9911                 }
9912                 // then make sure top/left isn't negative
9913                 if(x < vx){
9914                     x = vx;
9915                     moved = true;
9916                 }
9917                 if(y < vy){
9918                     y = vy;
9919                     moved = true;
9920                 }
9921                 return moved ? [x, y] : false;
9922             };
9923         }(),
9924
9925         // private
9926         adjustForConstraints : function(xy, parent, offsets){
9927             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9928         },
9929
9930         /**
9931          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9932          * document it aligns it to the viewport.
9933          * The position parameter is optional, and can be specified in any one of the following formats:
9934          * <ul>
9935          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9936          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9937          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9938          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9939          *   <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
9940          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9941          * </ul>
9942          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9943          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9944          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9945          * that specified in order to enforce the viewport constraints.
9946          * Following are all of the supported anchor positions:
9947     <pre>
9948     Value  Description
9949     -----  -----------------------------
9950     tl     The top left corner (default)
9951     t      The center of the top edge
9952     tr     The top right corner
9953     l      The center of the left edge
9954     c      In the center of the element
9955     r      The center of the right edge
9956     bl     The bottom left corner
9957     b      The center of the bottom edge
9958     br     The bottom right corner
9959     </pre>
9960     Example Usage:
9961     <pre><code>
9962     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9963     el.alignTo("other-el");
9964
9965     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9966     el.alignTo("other-el", "tr?");
9967
9968     // align the bottom right corner of el with the center left edge of other-el
9969     el.alignTo("other-el", "br-l?");
9970
9971     // align the center of el with the bottom left corner of other-el and
9972     // adjust the x position by -6 pixels (and the y position by 0)
9973     el.alignTo("other-el", "c-bl", [-6, 0]);
9974     </code></pre>
9975          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9976          * @param {String} position The position to align to.
9977          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9979          * @return {Roo.Element} this
9980          */
9981         alignTo : function(element, position, offsets, animate){
9982             var xy = this.getAlignToXY(element, position, offsets);
9983             this.setXY(xy, this.preanim(arguments, 3));
9984             return this;
9985         },
9986
9987         /**
9988          * Anchors an element to another element and realigns it when the window is resized.
9989          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9990          * @param {String} position The position to align to.
9991          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9992          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9993          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9994          * is a number, it is used as the buffer delay (defaults to 50ms).
9995          * @param {Function} callback The function to call after the animation finishes
9996          * @return {Roo.Element} this
9997          */
9998         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9999             var action = function(){
10000                 this.alignTo(el, alignment, offsets, animate);
10001                 Roo.callback(callback, this);
10002             };
10003             Roo.EventManager.onWindowResize(action, this);
10004             var tm = typeof monitorScroll;
10005             if(tm != 'undefined'){
10006                 Roo.EventManager.on(window, 'scroll', action, this,
10007                     {buffer: tm == 'number' ? monitorScroll : 50});
10008             }
10009             action.call(this); // align immediately
10010             return this;
10011         },
10012         /**
10013          * Clears any opacity settings from this element. Required in some cases for IE.
10014          * @return {Roo.Element} this
10015          */
10016         clearOpacity : function(){
10017             if (window.ActiveXObject) {
10018                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10019                     this.dom.style.filter = "";
10020                 }
10021             } else {
10022                 this.dom.style.opacity = "";
10023                 this.dom.style["-moz-opacity"] = "";
10024                 this.dom.style["-khtml-opacity"] = "";
10025             }
10026             return this;
10027         },
10028
10029         /**
10030          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10031          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10032          * @return {Roo.Element} this
10033          */
10034         hide : function(animate){
10035             this.setVisible(false, this.preanim(arguments, 0));
10036             return this;
10037         },
10038
10039         /**
10040         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10041         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10042          * @return {Roo.Element} this
10043          */
10044         show : function(animate){
10045             this.setVisible(true, this.preanim(arguments, 0));
10046             return this;
10047         },
10048
10049         /**
10050          * @private Test if size has a unit, otherwise appends the default
10051          */
10052         addUnits : function(size){
10053             return Roo.Element.addUnits(size, this.defaultUnit);
10054         },
10055
10056         /**
10057          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10058          * @return {Roo.Element} this
10059          */
10060         beginMeasure : function(){
10061             var el = this.dom;
10062             if(el.offsetWidth || el.offsetHeight){
10063                 return this; // offsets work already
10064             }
10065             var changed = [];
10066             var p = this.dom, b = document.body; // start with this element
10067             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10068                 var pe = Roo.get(p);
10069                 if(pe.getStyle('display') == 'none'){
10070                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10071                     p.style.visibility = "hidden";
10072                     p.style.display = "block";
10073                 }
10074                 p = p.parentNode;
10075             }
10076             this._measureChanged = changed;
10077             return this;
10078
10079         },
10080
10081         /**
10082          * Restores displays to before beginMeasure was called
10083          * @return {Roo.Element} this
10084          */
10085         endMeasure : function(){
10086             var changed = this._measureChanged;
10087             if(changed){
10088                 for(var i = 0, len = changed.length; i < len; i++) {
10089                     var r = changed[i];
10090                     r.el.style.visibility = r.visibility;
10091                     r.el.style.display = "none";
10092                 }
10093                 this._measureChanged = null;
10094             }
10095             return this;
10096         },
10097
10098         /**
10099         * Update the innerHTML of this element, optionally searching for and processing scripts
10100         * @param {String} html The new HTML
10101         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10102         * @param {Function} callback For async script loading you can be noticed when the update completes
10103         * @return {Roo.Element} this
10104          */
10105         update : function(html, loadScripts, callback){
10106             if(typeof html == "undefined"){
10107                 html = "";
10108             }
10109             if(loadScripts !== true){
10110                 this.dom.innerHTML = html;
10111                 if(typeof callback == "function"){
10112                     callback();
10113                 }
10114                 return this;
10115             }
10116             var id = Roo.id();
10117             var dom = this.dom;
10118
10119             html += '<span id="' + id + '"></span>';
10120
10121             E.onAvailable(id, function(){
10122                 var hd = document.getElementsByTagName("head")[0];
10123                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10124                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10125                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10126
10127                 var match;
10128                 while(match = re.exec(html)){
10129                     var attrs = match[1];
10130                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10131                     if(srcMatch && srcMatch[2]){
10132                        var s = document.createElement("script");
10133                        s.src = srcMatch[2];
10134                        var typeMatch = attrs.match(typeRe);
10135                        if(typeMatch && typeMatch[2]){
10136                            s.type = typeMatch[2];
10137                        }
10138                        hd.appendChild(s);
10139                     }else if(match[2] && match[2].length > 0){
10140                         if(window.execScript) {
10141                            window.execScript(match[2]);
10142                         } else {
10143                             /**
10144                              * eval:var:id
10145                              * eval:var:dom
10146                              * eval:var:html
10147                              * 
10148                              */
10149                            window.eval(match[2]);
10150                         }
10151                     }
10152                 }
10153                 var el = document.getElementById(id);
10154                 if(el){el.parentNode.removeChild(el);}
10155                 if(typeof callback == "function"){
10156                     callback();
10157                 }
10158             });
10159             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10160             return this;
10161         },
10162
10163         /**
10164          * Direct access to the UpdateManager update() method (takes the same parameters).
10165          * @param {String/Function} url The url for this request or a function to call to get the url
10166          * @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}
10167          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10168          * @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.
10169          * @return {Roo.Element} this
10170          */
10171         load : function(){
10172             var um = this.getUpdateManager();
10173             um.update.apply(um, arguments);
10174             return this;
10175         },
10176
10177         /**
10178         * Gets this element's UpdateManager
10179         * @return {Roo.UpdateManager} The UpdateManager
10180         */
10181         getUpdateManager : function(){
10182             if(!this.updateManager){
10183                 this.updateManager = new Roo.UpdateManager(this);
10184             }
10185             return this.updateManager;
10186         },
10187
10188         /**
10189          * Disables text selection for this element (normalized across browsers)
10190          * @return {Roo.Element} this
10191          */
10192         unselectable : function(){
10193             this.dom.unselectable = "on";
10194             this.swallowEvent("selectstart", true);
10195             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10196             this.addClass("x-unselectable");
10197             return this;
10198         },
10199
10200         /**
10201         * Calculates the x, y to center this element on the screen
10202         * @return {Array} The x, y values [x, y]
10203         */
10204         getCenterXY : function(){
10205             return this.getAlignToXY(document, 'c-c');
10206         },
10207
10208         /**
10209         * Centers the Element in either the viewport, or another Element.
10210         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10211         */
10212         center : function(centerIn){
10213             this.alignTo(centerIn || document, 'c-c');
10214             return this;
10215         },
10216
10217         /**
10218          * Tests various css rules/browsers to determine if this element uses a border box
10219          * @return {Boolean}
10220          */
10221         isBorderBox : function(){
10222             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10223         },
10224
10225         /**
10226          * Return a box {x, y, width, height} that can be used to set another elements
10227          * size/location to match this element.
10228          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10229          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10230          * @return {Object} box An object in the format {x, y, width, height}
10231          */
10232         getBox : function(contentBox, local){
10233             var xy;
10234             if(!local){
10235                 xy = this.getXY();
10236             }else{
10237                 var left = parseInt(this.getStyle("left"), 10) || 0;
10238                 var top = parseInt(this.getStyle("top"), 10) || 0;
10239                 xy = [left, top];
10240             }
10241             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10242             if(!contentBox){
10243                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10244             }else{
10245                 var l = this.getBorderWidth("l")+this.getPadding("l");
10246                 var r = this.getBorderWidth("r")+this.getPadding("r");
10247                 var t = this.getBorderWidth("t")+this.getPadding("t");
10248                 var b = this.getBorderWidth("b")+this.getPadding("b");
10249                 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)};
10250             }
10251             bx.right = bx.x + bx.width;
10252             bx.bottom = bx.y + bx.height;
10253             return bx;
10254         },
10255
10256         /**
10257          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10258          for more information about the sides.
10259          * @param {String} sides
10260          * @return {Number}
10261          */
10262         getFrameWidth : function(sides, onlyContentBox){
10263             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10264         },
10265
10266         /**
10267          * 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.
10268          * @param {Object} box The box to fill {x, y, width, height}
10269          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10270          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10271          * @return {Roo.Element} this
10272          */
10273         setBox : function(box, adjust, animate){
10274             var w = box.width, h = box.height;
10275             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10276                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10277                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10278             }
10279             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10280             return this;
10281         },
10282
10283         /**
10284          * Forces the browser to repaint this element
10285          * @return {Roo.Element} this
10286          */
10287          repaint : function(){
10288             var dom = this.dom;
10289             this.addClass("x-repaint");
10290             setTimeout(function(){
10291                 Roo.get(dom).removeClass("x-repaint");
10292             }, 1);
10293             return this;
10294         },
10295
10296         /**
10297          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10298          * then it returns the calculated width of the sides (see getPadding)
10299          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10300          * @return {Object/Number}
10301          */
10302         getMargins : function(side){
10303             if(!side){
10304                 return {
10305                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10306                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10307                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10308                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10309                 };
10310             }else{
10311                 return this.addStyles(side, El.margins);
10312              }
10313         },
10314
10315         // private
10316         addStyles : function(sides, styles){
10317             var val = 0, v, w;
10318             for(var i = 0, len = sides.length; i < len; i++){
10319                 v = this.getStyle(styles[sides.charAt(i)]);
10320                 if(v){
10321                      w = parseInt(v, 10);
10322                      if(w){ val += w; }
10323                 }
10324             }
10325             return val;
10326         },
10327
10328         /**
10329          * Creates a proxy element of this element
10330          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10331          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10332          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10333          * @return {Roo.Element} The new proxy element
10334          */
10335         createProxy : function(config, renderTo, matchBox){
10336             if(renderTo){
10337                 renderTo = Roo.getDom(renderTo);
10338             }else{
10339                 renderTo = document.body;
10340             }
10341             config = typeof config == "object" ?
10342                 config : {tag : "div", cls: config};
10343             var proxy = Roo.DomHelper.append(renderTo, config, true);
10344             if(matchBox){
10345                proxy.setBox(this.getBox());
10346             }
10347             return proxy;
10348         },
10349
10350         /**
10351          * Puts a mask over this element to disable user interaction. Requires core.css.
10352          * This method can only be applied to elements which accept child nodes.
10353          * @param {String} msg (optional) A message to display in the mask
10354          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10355          * @return {Element} The mask  element
10356          */
10357         mask : function(msg, msgCls)
10358         {
10359             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10360                 this.setStyle("position", "relative");
10361             }
10362             if(!this._mask){
10363                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10364             }
10365             
10366             this.addClass("x-masked");
10367             this._mask.setDisplayed(true);
10368             
10369             // we wander
10370             var z = 0;
10371             var dom = this.dom;
10372             while (dom && dom.style) {
10373                 if (!isNaN(parseInt(dom.style.zIndex))) {
10374                     z = Math.max(z, parseInt(dom.style.zIndex));
10375                 }
10376                 dom = dom.parentNode;
10377             }
10378             // if we are masking the body - then it hides everything..
10379             if (this.dom == document.body) {
10380                 z = 1000000;
10381                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10382                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10383             }
10384            
10385             if(typeof msg == 'string'){
10386                 if(!this._maskMsg){
10387                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10388                         cls: "roo-el-mask-msg", 
10389                         cn: [
10390                             {
10391                                 tag: 'i',
10392                                 cls: 'fa fa-spinner fa-spin'
10393                             },
10394                             {
10395                                 tag: 'div'
10396                             }   
10397                         ]
10398                     }, true);
10399                 }
10400                 var mm = this._maskMsg;
10401                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10402                 if (mm.dom.lastChild) { // weird IE issue?
10403                     mm.dom.lastChild.innerHTML = msg;
10404                 }
10405                 mm.setDisplayed(true);
10406                 mm.center(this);
10407                 mm.setStyle('z-index', z + 102);
10408             }
10409             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10410                 this._mask.setHeight(this.getHeight());
10411             }
10412             this._mask.setStyle('z-index', z + 100);
10413             
10414             return this._mask;
10415         },
10416
10417         /**
10418          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10419          * it is cached for reuse.
10420          */
10421         unmask : function(removeEl){
10422             if(this._mask){
10423                 if(removeEl === true){
10424                     this._mask.remove();
10425                     delete this._mask;
10426                     if(this._maskMsg){
10427                         this._maskMsg.remove();
10428                         delete this._maskMsg;
10429                     }
10430                 }else{
10431                     this._mask.setDisplayed(false);
10432                     if(this._maskMsg){
10433                         this._maskMsg.setDisplayed(false);
10434                     }
10435                 }
10436             }
10437             this.removeClass("x-masked");
10438         },
10439
10440         /**
10441          * Returns true if this element is masked
10442          * @return {Boolean}
10443          */
10444         isMasked : function(){
10445             return this._mask && this._mask.isVisible();
10446         },
10447
10448         /**
10449          * Creates an iframe shim for this element to keep selects and other windowed objects from
10450          * showing through.
10451          * @return {Roo.Element} The new shim element
10452          */
10453         createShim : function(){
10454             var el = document.createElement('iframe');
10455             el.frameBorder = 'no';
10456             el.className = 'roo-shim';
10457             if(Roo.isIE && Roo.isSecure){
10458                 el.src = Roo.SSL_SECURE_URL;
10459             }
10460             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10461             shim.autoBoxAdjust = false;
10462             return shim;
10463         },
10464
10465         /**
10466          * Removes this element from the DOM and deletes it from the cache
10467          */
10468         remove : function(){
10469             if(this.dom.parentNode){
10470                 this.dom.parentNode.removeChild(this.dom);
10471             }
10472             delete El.cache[this.dom.id];
10473         },
10474
10475         /**
10476          * Sets up event handlers to add and remove a css class when the mouse is over this element
10477          * @param {String} className
10478          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10479          * mouseout events for children elements
10480          * @return {Roo.Element} this
10481          */
10482         addClassOnOver : function(className, preventFlicker){
10483             this.on("mouseover", function(){
10484                 Roo.fly(this, '_internal').addClass(className);
10485             }, this.dom);
10486             var removeFn = function(e){
10487                 if(preventFlicker !== true || !e.within(this, true)){
10488                     Roo.fly(this, '_internal').removeClass(className);
10489                 }
10490             };
10491             this.on("mouseout", removeFn, this.dom);
10492             return this;
10493         },
10494
10495         /**
10496          * Sets up event handlers to add and remove a css class when this element has the focus
10497          * @param {String} className
10498          * @return {Roo.Element} this
10499          */
10500         addClassOnFocus : function(className){
10501             this.on("focus", function(){
10502                 Roo.fly(this, '_internal').addClass(className);
10503             }, this.dom);
10504             this.on("blur", function(){
10505                 Roo.fly(this, '_internal').removeClass(className);
10506             }, this.dom);
10507             return this;
10508         },
10509         /**
10510          * 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)
10511          * @param {String} className
10512          * @return {Roo.Element} this
10513          */
10514         addClassOnClick : function(className){
10515             var dom = this.dom;
10516             this.on("mousedown", function(){
10517                 Roo.fly(dom, '_internal').addClass(className);
10518                 var d = Roo.get(document);
10519                 var fn = function(){
10520                     Roo.fly(dom, '_internal').removeClass(className);
10521                     d.removeListener("mouseup", fn);
10522                 };
10523                 d.on("mouseup", fn);
10524             });
10525             return this;
10526         },
10527
10528         /**
10529          * Stops the specified event from bubbling and optionally prevents the default action
10530          * @param {String} eventName
10531          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10532          * @return {Roo.Element} this
10533          */
10534         swallowEvent : function(eventName, preventDefault){
10535             var fn = function(e){
10536                 e.stopPropagation();
10537                 if(preventDefault){
10538                     e.preventDefault();
10539                 }
10540             };
10541             if(eventName instanceof Array){
10542                 for(var i = 0, len = eventName.length; i < len; i++){
10543                      this.on(eventName[i], fn);
10544                 }
10545                 return this;
10546             }
10547             this.on(eventName, fn);
10548             return this;
10549         },
10550
10551         /**
10552          * @private
10553          */
10554         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10555
10556         /**
10557          * Sizes this element to its parent element's dimensions performing
10558          * neccessary box adjustments.
10559          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10560          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10561          * @return {Roo.Element} this
10562          */
10563         fitToParent : function(monitorResize, targetParent) {
10564           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10565           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10566           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10567             return this;
10568           }
10569           var p = Roo.get(targetParent || this.dom.parentNode);
10570           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10571           if (monitorResize === true) {
10572             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10573             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10574           }
10575           return this;
10576         },
10577
10578         /**
10579          * Gets the next sibling, skipping text nodes
10580          * @return {HTMLElement} The next sibling or null
10581          */
10582         getNextSibling : function(){
10583             var n = this.dom.nextSibling;
10584             while(n && n.nodeType != 1){
10585                 n = n.nextSibling;
10586             }
10587             return n;
10588         },
10589
10590         /**
10591          * Gets the previous sibling, skipping text nodes
10592          * @return {HTMLElement} The previous sibling or null
10593          */
10594         getPrevSibling : function(){
10595             var n = this.dom.previousSibling;
10596             while(n && n.nodeType != 1){
10597                 n = n.previousSibling;
10598             }
10599             return n;
10600         },
10601
10602
10603         /**
10604          * Appends the passed element(s) to this element
10605          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10606          * @return {Roo.Element} this
10607          */
10608         appendChild: function(el){
10609             el = Roo.get(el);
10610             el.appendTo(this);
10611             return this;
10612         },
10613
10614         /**
10615          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10616          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10617          * automatically generated with the specified attributes.
10618          * @param {HTMLElement} insertBefore (optional) a child element of this element
10619          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10620          * @return {Roo.Element} The new child element
10621          */
10622         createChild: function(config, insertBefore, returnDom){
10623             config = config || {tag:'div'};
10624             if(insertBefore){
10625                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10626             }
10627             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10628         },
10629
10630         /**
10631          * Appends this element to the passed element
10632          * @param {String/HTMLElement/Element} el The new parent element
10633          * @return {Roo.Element} this
10634          */
10635         appendTo: function(el){
10636             el = Roo.getDom(el);
10637             el.appendChild(this.dom);
10638             return this;
10639         },
10640
10641         /**
10642          * Inserts this element before the passed element in the DOM
10643          * @param {String/HTMLElement/Element} el The element to insert before
10644          * @return {Roo.Element} this
10645          */
10646         insertBefore: function(el){
10647             el = Roo.getDom(el);
10648             el.parentNode.insertBefore(this.dom, el);
10649             return this;
10650         },
10651
10652         /**
10653          * Inserts this element after the passed element in the DOM
10654          * @param {String/HTMLElement/Element} el The element to insert after
10655          * @return {Roo.Element} this
10656          */
10657         insertAfter: function(el){
10658             el = Roo.getDom(el);
10659             el.parentNode.insertBefore(this.dom, el.nextSibling);
10660             return this;
10661         },
10662
10663         /**
10664          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10665          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10666          * @return {Roo.Element} The new child
10667          */
10668         insertFirst: function(el, returnDom){
10669             el = el || {};
10670             if(typeof el == 'object' && !el.nodeType){ // dh config
10671                 return this.createChild(el, this.dom.firstChild, returnDom);
10672             }else{
10673                 el = Roo.getDom(el);
10674                 this.dom.insertBefore(el, this.dom.firstChild);
10675                 return !returnDom ? Roo.get(el) : el;
10676             }
10677         },
10678
10679         /**
10680          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10681          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10682          * @param {String} where (optional) 'before' or 'after' defaults to before
10683          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10684          * @return {Roo.Element} the inserted Element
10685          */
10686         insertSibling: function(el, where, returnDom){
10687             where = where ? where.toLowerCase() : 'before';
10688             el = el || {};
10689             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10690
10691             if(typeof el == 'object' && !el.nodeType){ // dh config
10692                 if(where == 'after' && !this.dom.nextSibling){
10693                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10694                 }else{
10695                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10696                 }
10697
10698             }else{
10699                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10700                             where == 'before' ? this.dom : this.dom.nextSibling);
10701                 if(!returnDom){
10702                     rt = Roo.get(rt);
10703                 }
10704             }
10705             return rt;
10706         },
10707
10708         /**
10709          * Creates and wraps this element with another element
10710          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10711          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10712          * @return {HTMLElement/Element} The newly created wrapper element
10713          */
10714         wrap: function(config, returnDom){
10715             if(!config){
10716                 config = {tag: "div"};
10717             }
10718             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10719             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10720             return newEl;
10721         },
10722
10723         /**
10724          * Replaces the passed element with this element
10725          * @param {String/HTMLElement/Element} el The element to replace
10726          * @return {Roo.Element} this
10727          */
10728         replace: function(el){
10729             el = Roo.get(el);
10730             this.insertBefore(el);
10731             el.remove();
10732             return this;
10733         },
10734
10735         /**
10736          * Inserts an html fragment into this element
10737          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10738          * @param {String} html The HTML fragment
10739          * @param {Boolean} returnEl True to return an Roo.Element
10740          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10741          */
10742         insertHtml : function(where, html, returnEl){
10743             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10744             return returnEl ? Roo.get(el) : el;
10745         },
10746
10747         /**
10748          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10749          * @param {Object} o The object with the attributes
10750          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10751          * @return {Roo.Element} this
10752          */
10753         set : function(o, useSet){
10754             var el = this.dom;
10755             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10756             for(var attr in o){
10757                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10758                 if(attr=="cls"){
10759                     el.className = o["cls"];
10760                 }else{
10761                     if(useSet) {
10762                         el.setAttribute(attr, o[attr]);
10763                     } else {
10764                         el[attr] = o[attr];
10765                     }
10766                 }
10767             }
10768             if(o.style){
10769                 Roo.DomHelper.applyStyles(el, o.style);
10770             }
10771             return this;
10772         },
10773
10774         /**
10775          * Convenience method for constructing a KeyMap
10776          * @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:
10777          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10778          * @param {Function} fn The function to call
10779          * @param {Object} scope (optional) The scope of the function
10780          * @return {Roo.KeyMap} The KeyMap created
10781          */
10782         addKeyListener : function(key, fn, scope){
10783             var config;
10784             if(typeof key != "object" || key instanceof Array){
10785                 config = {
10786                     key: key,
10787                     fn: fn,
10788                     scope: scope
10789                 };
10790             }else{
10791                 config = {
10792                     key : key.key,
10793                     shift : key.shift,
10794                     ctrl : key.ctrl,
10795                     alt : key.alt,
10796                     fn: fn,
10797                     scope: scope
10798                 };
10799             }
10800             return new Roo.KeyMap(this, config);
10801         },
10802
10803         /**
10804          * Creates a KeyMap for this element
10805          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10806          * @return {Roo.KeyMap} The KeyMap created
10807          */
10808         addKeyMap : function(config){
10809             return new Roo.KeyMap(this, config);
10810         },
10811
10812         /**
10813          * Returns true if this element is scrollable.
10814          * @return {Boolean}
10815          */
10816          isScrollable : function(){
10817             var dom = this.dom;
10818             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10819         },
10820
10821         /**
10822          * 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().
10823          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10824          * @param {Number} value The new scroll value
10825          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10826          * @return {Element} this
10827          */
10828
10829         scrollTo : function(side, value, animate){
10830             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10831             if(!animate || !A){
10832                 this.dom[prop] = value;
10833             }else{
10834                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10835                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10836             }
10837             return this;
10838         },
10839
10840         /**
10841          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10842          * within this element's scrollable range.
10843          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10844          * @param {Number} distance How far to scroll the element in pixels
10845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10846          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10847          * was scrolled as far as it could go.
10848          */
10849          scroll : function(direction, distance, animate){
10850              if(!this.isScrollable()){
10851                  return;
10852              }
10853              var el = this.dom;
10854              var l = el.scrollLeft, t = el.scrollTop;
10855              var w = el.scrollWidth, h = el.scrollHeight;
10856              var cw = el.clientWidth, ch = el.clientHeight;
10857              direction = direction.toLowerCase();
10858              var scrolled = false;
10859              var a = this.preanim(arguments, 2);
10860              switch(direction){
10861                  case "l":
10862                  case "left":
10863                      if(w - l > cw){
10864                          var v = Math.min(l + distance, w-cw);
10865                          this.scrollTo("left", v, a);
10866                          scrolled = true;
10867                      }
10868                      break;
10869                 case "r":
10870                 case "right":
10871                      if(l > 0){
10872                          var v = Math.max(l - distance, 0);
10873                          this.scrollTo("left", v, a);
10874                          scrolled = true;
10875                      }
10876                      break;
10877                 case "t":
10878                 case "top":
10879                 case "up":
10880                      if(t > 0){
10881                          var v = Math.max(t - distance, 0);
10882                          this.scrollTo("top", v, a);
10883                          scrolled = true;
10884                      }
10885                      break;
10886                 case "b":
10887                 case "bottom":
10888                 case "down":
10889                      if(h - t > ch){
10890                          var v = Math.min(t + distance, h-ch);
10891                          this.scrollTo("top", v, a);
10892                          scrolled = true;
10893                      }
10894                      break;
10895              }
10896              return scrolled;
10897         },
10898
10899         /**
10900          * Translates the passed page coordinates into left/top css values for this element
10901          * @param {Number/Array} x The page x or an array containing [x, y]
10902          * @param {Number} y The page y
10903          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10904          */
10905         translatePoints : function(x, y){
10906             if(typeof x == 'object' || x instanceof Array){
10907                 y = x[1]; x = x[0];
10908             }
10909             var p = this.getStyle('position');
10910             var o = this.getXY();
10911
10912             var l = parseInt(this.getStyle('left'), 10);
10913             var t = parseInt(this.getStyle('top'), 10);
10914
10915             if(isNaN(l)){
10916                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10917             }
10918             if(isNaN(t)){
10919                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10920             }
10921
10922             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10923         },
10924
10925         /**
10926          * Returns the current scroll position of the element.
10927          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10928          */
10929         getScroll : function(){
10930             var d = this.dom, doc = document;
10931             if(d == doc || d == doc.body){
10932                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10933                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10934                 return {left: l, top: t};
10935             }else{
10936                 return {left: d.scrollLeft, top: d.scrollTop};
10937             }
10938         },
10939
10940         /**
10941          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10942          * are convert to standard 6 digit hex color.
10943          * @param {String} attr The css attribute
10944          * @param {String} defaultValue The default value to use when a valid color isn't found
10945          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10946          * YUI color anims.
10947          */
10948         getColor : function(attr, defaultValue, prefix){
10949             var v = this.getStyle(attr);
10950             if(!v || v == "transparent" || v == "inherit") {
10951                 return defaultValue;
10952             }
10953             var color = typeof prefix == "undefined" ? "#" : prefix;
10954             if(v.substr(0, 4) == "rgb("){
10955                 var rvs = v.slice(4, v.length -1).split(",");
10956                 for(var i = 0; i < 3; i++){
10957                     var h = parseInt(rvs[i]).toString(16);
10958                     if(h < 16){
10959                         h = "0" + h;
10960                     }
10961                     color += h;
10962                 }
10963             } else {
10964                 if(v.substr(0, 1) == "#"){
10965                     if(v.length == 4) {
10966                         for(var i = 1; i < 4; i++){
10967                             var c = v.charAt(i);
10968                             color +=  c + c;
10969                         }
10970                     }else if(v.length == 7){
10971                         color += v.substr(1);
10972                     }
10973                 }
10974             }
10975             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10976         },
10977
10978         /**
10979          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10980          * gradient background, rounded corners and a 4-way shadow.
10981          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10982          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10983          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10984          * @return {Roo.Element} this
10985          */
10986         boxWrap : function(cls){
10987             cls = cls || 'x-box';
10988             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10989             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10990             return el;
10991         },
10992
10993         /**
10994          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10995          * @param {String} namespace The namespace in which to look for the attribute
10996          * @param {String} name The attribute name
10997          * @return {String} The attribute value
10998          */
10999         getAttributeNS : Roo.isIE ? function(ns, name){
11000             var d = this.dom;
11001             var type = typeof d[ns+":"+name];
11002             if(type != 'undefined' && type != 'unknown'){
11003                 return d[ns+":"+name];
11004             }
11005             return d[name];
11006         } : function(ns, name){
11007             var d = this.dom;
11008             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11009         },
11010         
11011         
11012         /**
11013          * Sets or Returns the value the dom attribute value
11014          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11015          * @param {String} value (optional) The value to set the attribute to
11016          * @return {String} The attribute value
11017          */
11018         attr : function(name){
11019             if (arguments.length > 1) {
11020                 this.dom.setAttribute(name, arguments[1]);
11021                 return arguments[1];
11022             }
11023             if (typeof(name) == 'object') {
11024                 for(var i in name) {
11025                     this.attr(i, name[i]);
11026                 }
11027                 return name;
11028             }
11029             
11030             
11031             if (!this.dom.hasAttribute(name)) {
11032                 return undefined;
11033             }
11034             return this.dom.getAttribute(name);
11035         }
11036         
11037         
11038         
11039     };
11040
11041     var ep = El.prototype;
11042
11043     /**
11044      * Appends an event handler (Shorthand for addListener)
11045      * @param {String}   eventName     The type of event to append
11046      * @param {Function} fn        The method the event invokes
11047      * @param {Object} scope       (optional) The scope (this object) of the fn
11048      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11049      * @method
11050      */
11051     ep.on = ep.addListener;
11052         // backwards compat
11053     ep.mon = ep.addListener;
11054
11055     /**
11056      * Removes an event handler from this element (shorthand for removeListener)
11057      * @param {String} eventName the type of event to remove
11058      * @param {Function} fn the method the event invokes
11059      * @return {Roo.Element} this
11060      * @method
11061      */
11062     ep.un = ep.removeListener;
11063
11064     /**
11065      * true to automatically adjust width and height settings for box-model issues (default to true)
11066      */
11067     ep.autoBoxAdjust = true;
11068
11069     // private
11070     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11071
11072     // private
11073     El.addUnits = function(v, defaultUnit){
11074         if(v === "" || v == "auto"){
11075             return v;
11076         }
11077         if(v === undefined){
11078             return '';
11079         }
11080         if(typeof v == "number" || !El.unitPattern.test(v)){
11081             return v + (defaultUnit || 'px');
11082         }
11083         return v;
11084     };
11085
11086     // special markup used throughout Roo when box wrapping elements
11087     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>';
11088     /**
11089      * Visibility mode constant - Use visibility to hide element
11090      * @static
11091      * @type Number
11092      */
11093     El.VISIBILITY = 1;
11094     /**
11095      * Visibility mode constant - Use display to hide element
11096      * @static
11097      * @type Number
11098      */
11099     El.DISPLAY = 2;
11100
11101     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11102     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11103     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11104
11105
11106
11107     /**
11108      * @private
11109      */
11110     El.cache = {};
11111
11112     var docEl;
11113
11114     /**
11115      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11116      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11117      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11118      * @return {Element} The Element object
11119      * @static
11120      */
11121     El.get = function(el){
11122         var ex, elm, id;
11123         if(!el){ return null; }
11124         if(typeof el == "string"){ // element id
11125             if(!(elm = document.getElementById(el))){
11126                 return null;
11127             }
11128             if(ex = El.cache[el]){
11129                 ex.dom = elm;
11130             }else{
11131                 ex = El.cache[el] = new El(elm);
11132             }
11133             return ex;
11134         }else if(el.tagName){ // dom element
11135             if(!(id = el.id)){
11136                 id = Roo.id(el);
11137             }
11138             if(ex = El.cache[id]){
11139                 ex.dom = el;
11140             }else{
11141                 ex = El.cache[id] = new El(el);
11142             }
11143             return ex;
11144         }else if(el instanceof El){
11145             if(el != docEl){
11146                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11147                                                               // catch case where it hasn't been appended
11148                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11149             }
11150             return el;
11151         }else if(el.isComposite){
11152             return el;
11153         }else if(el instanceof Array){
11154             return El.select(el);
11155         }else if(el == document){
11156             // create a bogus element object representing the document object
11157             if(!docEl){
11158                 var f = function(){};
11159                 f.prototype = El.prototype;
11160                 docEl = new f();
11161                 docEl.dom = document;
11162             }
11163             return docEl;
11164         }
11165         return null;
11166     };
11167
11168     // private
11169     El.uncache = function(el){
11170         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11171             if(a[i]){
11172                 delete El.cache[a[i].id || a[i]];
11173             }
11174         }
11175     };
11176
11177     // private
11178     // Garbage collection - uncache elements/purge listeners on orphaned elements
11179     // so we don't hold a reference and cause the browser to retain them
11180     El.garbageCollect = function(){
11181         if(!Roo.enableGarbageCollector){
11182             clearInterval(El.collectorThread);
11183             return;
11184         }
11185         for(var eid in El.cache){
11186             var el = El.cache[eid], d = el.dom;
11187             // -------------------------------------------------------
11188             // Determining what is garbage:
11189             // -------------------------------------------------------
11190             // !d
11191             // dom node is null, definitely garbage
11192             // -------------------------------------------------------
11193             // !d.parentNode
11194             // no parentNode == direct orphan, definitely garbage
11195             // -------------------------------------------------------
11196             // !d.offsetParent && !document.getElementById(eid)
11197             // display none elements have no offsetParent so we will
11198             // also try to look it up by it's id. However, check
11199             // offsetParent first so we don't do unneeded lookups.
11200             // This enables collection of elements that are not orphans
11201             // directly, but somewhere up the line they have an orphan
11202             // parent.
11203             // -------------------------------------------------------
11204             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11205                 delete El.cache[eid];
11206                 if(d && Roo.enableListenerCollection){
11207                     E.purgeElement(d);
11208                 }
11209             }
11210         }
11211     }
11212     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11213
11214
11215     // dom is optional
11216     El.Flyweight = function(dom){
11217         this.dom = dom;
11218     };
11219     El.Flyweight.prototype = El.prototype;
11220
11221     El._flyweights = {};
11222     /**
11223      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11224      * the dom node can be overwritten by other code.
11225      * @param {String/HTMLElement} el The dom node or id
11226      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11227      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11228      * @static
11229      * @return {Element} The shared Element object
11230      */
11231     El.fly = function(el, named){
11232         named = named || '_global';
11233         el = Roo.getDom(el);
11234         if(!el){
11235             return null;
11236         }
11237         if(!El._flyweights[named]){
11238             El._flyweights[named] = new El.Flyweight();
11239         }
11240         El._flyweights[named].dom = el;
11241         return El._flyweights[named];
11242     };
11243
11244     /**
11245      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11246      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11247      * Shorthand of {@link Roo.Element#get}
11248      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11249      * @return {Element} The Element object
11250      * @member Roo
11251      * @method get
11252      */
11253     Roo.get = El.get;
11254     /**
11255      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11256      * the dom node can be overwritten by other code.
11257      * Shorthand of {@link Roo.Element#fly}
11258      * @param {String/HTMLElement} el The dom node or id
11259      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11260      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11261      * @static
11262      * @return {Element} The shared Element object
11263      * @member Roo
11264      * @method fly
11265      */
11266     Roo.fly = El.fly;
11267
11268     // speedy lookup for elements never to box adjust
11269     var noBoxAdjust = Roo.isStrict ? {
11270         select:1
11271     } : {
11272         input:1, select:1, textarea:1
11273     };
11274     if(Roo.isIE || Roo.isGecko){
11275         noBoxAdjust['button'] = 1;
11276     }
11277
11278
11279     Roo.EventManager.on(window, 'unload', function(){
11280         delete El.cache;
11281         delete El._flyweights;
11282     });
11283 })();
11284
11285
11286
11287
11288 if(Roo.DomQuery){
11289     Roo.Element.selectorFunction = Roo.DomQuery.select;
11290 }
11291
11292 Roo.Element.select = function(selector, unique, root){
11293     var els;
11294     if(typeof selector == "string"){
11295         els = Roo.Element.selectorFunction(selector, root);
11296     }else if(selector.length !== undefined){
11297         els = selector;
11298     }else{
11299         throw "Invalid selector";
11300     }
11301     if(unique === true){
11302         return new Roo.CompositeElement(els);
11303     }else{
11304         return new Roo.CompositeElementLite(els);
11305     }
11306 };
11307 /**
11308  * Selects elements based on the passed CSS selector to enable working on them as 1.
11309  * @param {String/Array} selector The CSS selector or an array of elements
11310  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11311  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11312  * @return {CompositeElementLite/CompositeElement}
11313  * @member Roo
11314  * @method select
11315  */
11316 Roo.select = Roo.Element.select;
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331 /*
11332  * Based on:
11333  * Ext JS Library 1.1.1
11334  * Copyright(c) 2006-2007, Ext JS, LLC.
11335  *
11336  * Originally Released Under LGPL - original licence link has changed is not relivant.
11337  *
11338  * Fork - LGPL
11339  * <script type="text/javascript">
11340  */
11341
11342
11343
11344 //Notifies Element that fx methods are available
11345 Roo.enableFx = true;
11346
11347 /**
11348  * @class Roo.Fx
11349  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11350  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11351  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11352  * Element effects to work.</p><br/>
11353  *
11354  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11355  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11356  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11357  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11358  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11359  * expected results and should be done with care.</p><br/>
11360  *
11361  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11362  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11363 <pre>
11364 Value  Description
11365 -----  -----------------------------
11366 tl     The top left corner
11367 t      The center of the top edge
11368 tr     The top right corner
11369 l      The center of the left edge
11370 r      The center of the right edge
11371 bl     The bottom left corner
11372 b      The center of the bottom edge
11373 br     The bottom right corner
11374 </pre>
11375  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11376  * below are common options that can be passed to any Fx method.</b>
11377  * @cfg {Function} callback A function called when the effect is finished
11378  * @cfg {Object} scope The scope of the effect function
11379  * @cfg {String} easing A valid Easing value for the effect
11380  * @cfg {String} afterCls A css class to apply after the effect
11381  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11382  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11383  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11384  * effects that end with the element being visually hidden, ignored otherwise)
11385  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11386  * a function which returns such a specification that will be applied to the Element after the effect finishes
11387  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11388  * @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
11389  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11390  */
11391 Roo.Fx = {
11392         /**
11393          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11394          * origin for the slide effect.  This function automatically handles wrapping the element with
11395          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11396          * Usage:
11397          *<pre><code>
11398 // default: slide the element in from the top
11399 el.slideIn();
11400
11401 // custom: slide the element in from the right with a 2-second duration
11402 el.slideIn('r', { duration: 2 });
11403
11404 // common config options shown with default values
11405 el.slideIn('t', {
11406     easing: 'easeOut',
11407     duration: .5
11408 });
11409 </code></pre>
11410          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11411          * @param {Object} options (optional) Object literal with any of the Fx config options
11412          * @return {Roo.Element} The Element
11413          */
11414     slideIn : function(anchor, o){
11415         var el = this.getFxEl();
11416         o = o || {};
11417
11418         el.queueFx(o, function(){
11419
11420             anchor = anchor || "t";
11421
11422             // fix display to visibility
11423             this.fixDisplay();
11424
11425             // restore values after effect
11426             var r = this.getFxRestore();
11427             var b = this.getBox();
11428             // fixed size for slide
11429             this.setSize(b);
11430
11431             // wrap if needed
11432             var wrap = this.fxWrap(r.pos, o, "hidden");
11433
11434             var st = this.dom.style;
11435             st.visibility = "visible";
11436             st.position = "absolute";
11437
11438             // clear out temp styles after slide and unwrap
11439             var after = function(){
11440                 el.fxUnwrap(wrap, r.pos, o);
11441                 st.width = r.width;
11442                 st.height = r.height;
11443                 el.afterFx(o);
11444             };
11445             // time to calc the positions
11446             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11447
11448             switch(anchor.toLowerCase()){
11449                 case "t":
11450                     wrap.setSize(b.width, 0);
11451                     st.left = st.bottom = "0";
11452                     a = {height: bh};
11453                 break;
11454                 case "l":
11455                     wrap.setSize(0, b.height);
11456                     st.right = st.top = "0";
11457                     a = {width: bw};
11458                 break;
11459                 case "r":
11460                     wrap.setSize(0, b.height);
11461                     wrap.setX(b.right);
11462                     st.left = st.top = "0";
11463                     a = {width: bw, points: pt};
11464                 break;
11465                 case "b":
11466                     wrap.setSize(b.width, 0);
11467                     wrap.setY(b.bottom);
11468                     st.left = st.top = "0";
11469                     a = {height: bh, points: pt};
11470                 break;
11471                 case "tl":
11472                     wrap.setSize(0, 0);
11473                     st.right = st.bottom = "0";
11474                     a = {width: bw, height: bh};
11475                 break;
11476                 case "bl":
11477                     wrap.setSize(0, 0);
11478                     wrap.setY(b.y+b.height);
11479                     st.right = st.top = "0";
11480                     a = {width: bw, height: bh, points: pt};
11481                 break;
11482                 case "br":
11483                     wrap.setSize(0, 0);
11484                     wrap.setXY([b.right, b.bottom]);
11485                     st.left = st.top = "0";
11486                     a = {width: bw, height: bh, points: pt};
11487                 break;
11488                 case "tr":
11489                     wrap.setSize(0, 0);
11490                     wrap.setX(b.x+b.width);
11491                     st.left = st.bottom = "0";
11492                     a = {width: bw, height: bh, points: pt};
11493                 break;
11494             }
11495             this.dom.style.visibility = "visible";
11496             wrap.show();
11497
11498             arguments.callee.anim = wrap.fxanim(a,
11499                 o,
11500                 'motion',
11501                 .5,
11502                 'easeOut', after);
11503         });
11504         return this;
11505     },
11506     
11507         /**
11508          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11509          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11510          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11511          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11512          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11513          * Usage:
11514          *<pre><code>
11515 // default: slide the element out to the top
11516 el.slideOut();
11517
11518 // custom: slide the element out to the right with a 2-second duration
11519 el.slideOut('r', { duration: 2 });
11520
11521 // common config options shown with default values
11522 el.slideOut('t', {
11523     easing: 'easeOut',
11524     duration: .5,
11525     remove: false,
11526     useDisplay: false
11527 });
11528 </code></pre>
11529          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11530          * @param {Object} options (optional) Object literal with any of the Fx config options
11531          * @return {Roo.Element} The Element
11532          */
11533     slideOut : function(anchor, o){
11534         var el = this.getFxEl();
11535         o = o || {};
11536
11537         el.queueFx(o, function(){
11538
11539             anchor = anchor || "t";
11540
11541             // restore values after effect
11542             var r = this.getFxRestore();
11543             
11544             var b = this.getBox();
11545             // fixed size for slide
11546             this.setSize(b);
11547
11548             // wrap if needed
11549             var wrap = this.fxWrap(r.pos, o, "visible");
11550
11551             var st = this.dom.style;
11552             st.visibility = "visible";
11553             st.position = "absolute";
11554
11555             wrap.setSize(b);
11556
11557             var after = function(){
11558                 if(o.useDisplay){
11559                     el.setDisplayed(false);
11560                 }else{
11561                     el.hide();
11562                 }
11563
11564                 el.fxUnwrap(wrap, r.pos, o);
11565
11566                 st.width = r.width;
11567                 st.height = r.height;
11568
11569                 el.afterFx(o);
11570             };
11571
11572             var a, zero = {to: 0};
11573             switch(anchor.toLowerCase()){
11574                 case "t":
11575                     st.left = st.bottom = "0";
11576                     a = {height: zero};
11577                 break;
11578                 case "l":
11579                     st.right = st.top = "0";
11580                     a = {width: zero};
11581                 break;
11582                 case "r":
11583                     st.left = st.top = "0";
11584                     a = {width: zero, points: {to:[b.right, b.y]}};
11585                 break;
11586                 case "b":
11587                     st.left = st.top = "0";
11588                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11589                 break;
11590                 case "tl":
11591                     st.right = st.bottom = "0";
11592                     a = {width: zero, height: zero};
11593                 break;
11594                 case "bl":
11595                     st.right = st.top = "0";
11596                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11597                 break;
11598                 case "br":
11599                     st.left = st.top = "0";
11600                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11601                 break;
11602                 case "tr":
11603                     st.left = st.bottom = "0";
11604                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11605                 break;
11606             }
11607
11608             arguments.callee.anim = wrap.fxanim(a,
11609                 o,
11610                 'motion',
11611                 .5,
11612                 "easeOut", after);
11613         });
11614         return this;
11615     },
11616
11617         /**
11618          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11619          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11620          * The element must be removed from the DOM using the 'remove' config option if desired.
11621          * Usage:
11622          *<pre><code>
11623 // default
11624 el.puff();
11625
11626 // common config options shown with default values
11627 el.puff({
11628     easing: 'easeOut',
11629     duration: .5,
11630     remove: false,
11631     useDisplay: false
11632 });
11633 </code></pre>
11634          * @param {Object} options (optional) Object literal with any of the Fx config options
11635          * @return {Roo.Element} The Element
11636          */
11637     puff : function(o){
11638         var el = this.getFxEl();
11639         o = o || {};
11640
11641         el.queueFx(o, function(){
11642             this.clearOpacity();
11643             this.show();
11644
11645             // restore values after effect
11646             var r = this.getFxRestore();
11647             var st = this.dom.style;
11648
11649             var after = function(){
11650                 if(o.useDisplay){
11651                     el.setDisplayed(false);
11652                 }else{
11653                     el.hide();
11654                 }
11655
11656                 el.clearOpacity();
11657
11658                 el.setPositioning(r.pos);
11659                 st.width = r.width;
11660                 st.height = r.height;
11661                 st.fontSize = '';
11662                 el.afterFx(o);
11663             };
11664
11665             var width = this.getWidth();
11666             var height = this.getHeight();
11667
11668             arguments.callee.anim = this.fxanim({
11669                     width : {to: this.adjustWidth(width * 2)},
11670                     height : {to: this.adjustHeight(height * 2)},
11671                     points : {by: [-(width * .5), -(height * .5)]},
11672                     opacity : {to: 0},
11673                     fontSize: {to:200, unit: "%"}
11674                 },
11675                 o,
11676                 'motion',
11677                 .5,
11678                 "easeOut", after);
11679         });
11680         return this;
11681     },
11682
11683         /**
11684          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11685          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11686          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11687          * Usage:
11688          *<pre><code>
11689 // default
11690 el.switchOff();
11691
11692 // all config options shown with default values
11693 el.switchOff({
11694     easing: 'easeIn',
11695     duration: .3,
11696     remove: false,
11697     useDisplay: false
11698 });
11699 </code></pre>
11700          * @param {Object} options (optional) Object literal with any of the Fx config options
11701          * @return {Roo.Element} The Element
11702          */
11703     switchOff : function(o){
11704         var el = this.getFxEl();
11705         o = o || {};
11706
11707         el.queueFx(o, function(){
11708             this.clearOpacity();
11709             this.clip();
11710
11711             // restore values after effect
11712             var r = this.getFxRestore();
11713             var st = this.dom.style;
11714
11715             var after = function(){
11716                 if(o.useDisplay){
11717                     el.setDisplayed(false);
11718                 }else{
11719                     el.hide();
11720                 }
11721
11722                 el.clearOpacity();
11723                 el.setPositioning(r.pos);
11724                 st.width = r.width;
11725                 st.height = r.height;
11726
11727                 el.afterFx(o);
11728             };
11729
11730             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11731                 this.clearOpacity();
11732                 (function(){
11733                     this.fxanim({
11734                         height:{to:1},
11735                         points:{by:[0, this.getHeight() * .5]}
11736                     }, o, 'motion', 0.3, 'easeIn', after);
11737                 }).defer(100, this);
11738             });
11739         });
11740         return this;
11741     },
11742
11743     /**
11744      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11745      * changed using the "attr" config option) and then fading back to the original color. If no original
11746      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11747      * Usage:
11748 <pre><code>
11749 // default: highlight background to yellow
11750 el.highlight();
11751
11752 // custom: highlight foreground text to blue for 2 seconds
11753 el.highlight("0000ff", { attr: 'color', duration: 2 });
11754
11755 // common config options shown with default values
11756 el.highlight("ffff9c", {
11757     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11758     endColor: (current color) or "ffffff",
11759     easing: 'easeIn',
11760     duration: 1
11761 });
11762 </code></pre>
11763      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11764      * @param {Object} options (optional) Object literal with any of the Fx config options
11765      * @return {Roo.Element} The Element
11766      */ 
11767     highlight : function(color, o){
11768         var el = this.getFxEl();
11769         o = o || {};
11770
11771         el.queueFx(o, function(){
11772             color = color || "ffff9c";
11773             attr = o.attr || "backgroundColor";
11774
11775             this.clearOpacity();
11776             this.show();
11777
11778             var origColor = this.getColor(attr);
11779             var restoreColor = this.dom.style[attr];
11780             endColor = (o.endColor || origColor) || "ffffff";
11781
11782             var after = function(){
11783                 el.dom.style[attr] = restoreColor;
11784                 el.afterFx(o);
11785             };
11786
11787             var a = {};
11788             a[attr] = {from: color, to: endColor};
11789             arguments.callee.anim = this.fxanim(a,
11790                 o,
11791                 'color',
11792                 1,
11793                 'easeIn', after);
11794         });
11795         return this;
11796     },
11797
11798    /**
11799     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11800     * Usage:
11801 <pre><code>
11802 // default: a single light blue ripple
11803 el.frame();
11804
11805 // custom: 3 red ripples lasting 3 seconds total
11806 el.frame("ff0000", 3, { duration: 3 });
11807
11808 // common config options shown with default values
11809 el.frame("C3DAF9", 1, {
11810     duration: 1 //duration of entire animation (not each individual ripple)
11811     // Note: Easing is not configurable and will be ignored if included
11812 });
11813 </code></pre>
11814     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11815     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11816     * @param {Object} options (optional) Object literal with any of the Fx config options
11817     * @return {Roo.Element} The Element
11818     */
11819     frame : function(color, count, o){
11820         var el = this.getFxEl();
11821         o = o || {};
11822
11823         el.queueFx(o, function(){
11824             color = color || "#C3DAF9";
11825             if(color.length == 6){
11826                 color = "#" + color;
11827             }
11828             count = count || 1;
11829             duration = o.duration || 1;
11830             this.show();
11831
11832             var b = this.getBox();
11833             var animFn = function(){
11834                 var proxy = this.createProxy({
11835
11836                      style:{
11837                         visbility:"hidden",
11838                         position:"absolute",
11839                         "z-index":"35000", // yee haw
11840                         border:"0px solid " + color
11841                      }
11842                   });
11843                 var scale = Roo.isBorderBox ? 2 : 1;
11844                 proxy.animate({
11845                     top:{from:b.y, to:b.y - 20},
11846                     left:{from:b.x, to:b.x - 20},
11847                     borderWidth:{from:0, to:10},
11848                     opacity:{from:1, to:0},
11849                     height:{from:b.height, to:(b.height + (20*scale))},
11850                     width:{from:b.width, to:(b.width + (20*scale))}
11851                 }, duration, function(){
11852                     proxy.remove();
11853                 });
11854                 if(--count > 0){
11855                      animFn.defer((duration/2)*1000, this);
11856                 }else{
11857                     el.afterFx(o);
11858                 }
11859             };
11860             animFn.call(this);
11861         });
11862         return this;
11863     },
11864
11865    /**
11866     * Creates a pause before any subsequent queued effects begin.  If there are
11867     * no effects queued after the pause it will have no effect.
11868     * Usage:
11869 <pre><code>
11870 el.pause(1);
11871 </code></pre>
11872     * @param {Number} seconds The length of time to pause (in seconds)
11873     * @return {Roo.Element} The Element
11874     */
11875     pause : function(seconds){
11876         var el = this.getFxEl();
11877         var o = {};
11878
11879         el.queueFx(o, function(){
11880             setTimeout(function(){
11881                 el.afterFx(o);
11882             }, seconds * 1000);
11883         });
11884         return this;
11885     },
11886
11887    /**
11888     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11889     * using the "endOpacity" config option.
11890     * Usage:
11891 <pre><code>
11892 // default: fade in from opacity 0 to 100%
11893 el.fadeIn();
11894
11895 // custom: fade in from opacity 0 to 75% over 2 seconds
11896 el.fadeIn({ endOpacity: .75, duration: 2});
11897
11898 // common config options shown with default values
11899 el.fadeIn({
11900     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11901     easing: 'easeOut',
11902     duration: .5
11903 });
11904 </code></pre>
11905     * @param {Object} options (optional) Object literal with any of the Fx config options
11906     * @return {Roo.Element} The Element
11907     */
11908     fadeIn : function(o){
11909         var el = this.getFxEl();
11910         o = o || {};
11911         el.queueFx(o, function(){
11912             this.setOpacity(0);
11913             this.fixDisplay();
11914             this.dom.style.visibility = 'visible';
11915             var to = o.endOpacity || 1;
11916             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11917                 o, null, .5, "easeOut", function(){
11918                 if(to == 1){
11919                     this.clearOpacity();
11920                 }
11921                 el.afterFx(o);
11922             });
11923         });
11924         return this;
11925     },
11926
11927    /**
11928     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11929     * using the "endOpacity" config option.
11930     * Usage:
11931 <pre><code>
11932 // default: fade out from the element's current opacity to 0
11933 el.fadeOut();
11934
11935 // custom: fade out from the element's current opacity to 25% over 2 seconds
11936 el.fadeOut({ endOpacity: .25, duration: 2});
11937
11938 // common config options shown with default values
11939 el.fadeOut({
11940     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11941     easing: 'easeOut',
11942     duration: .5
11943     remove: false,
11944     useDisplay: false
11945 });
11946 </code></pre>
11947     * @param {Object} options (optional) Object literal with any of the Fx config options
11948     * @return {Roo.Element} The Element
11949     */
11950     fadeOut : function(o){
11951         var el = this.getFxEl();
11952         o = o || {};
11953         el.queueFx(o, function(){
11954             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11955                 o, null, .5, "easeOut", function(){
11956                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11957                      this.dom.style.display = "none";
11958                 }else{
11959                      this.dom.style.visibility = "hidden";
11960                 }
11961                 this.clearOpacity();
11962                 el.afterFx(o);
11963             });
11964         });
11965         return this;
11966     },
11967
11968    /**
11969     * Animates the transition of an element's dimensions from a starting height/width
11970     * to an ending height/width.
11971     * Usage:
11972 <pre><code>
11973 // change height and width to 100x100 pixels
11974 el.scale(100, 100);
11975
11976 // common config options shown with default values.  The height and width will default to
11977 // the element's existing values if passed as null.
11978 el.scale(
11979     [element's width],
11980     [element's height], {
11981     easing: 'easeOut',
11982     duration: .35
11983 });
11984 </code></pre>
11985     * @param {Number} width  The new width (pass undefined to keep the original width)
11986     * @param {Number} height  The new height (pass undefined to keep the original height)
11987     * @param {Object} options (optional) Object literal with any of the Fx config options
11988     * @return {Roo.Element} The Element
11989     */
11990     scale : function(w, h, o){
11991         this.shift(Roo.apply({}, o, {
11992             width: w,
11993             height: h
11994         }));
11995         return this;
11996     },
11997
11998    /**
11999     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12000     * Any of these properties not specified in the config object will not be changed.  This effect 
12001     * requires that at least one new dimension, position or opacity setting must be passed in on
12002     * the config object in order for the function to have any effect.
12003     * Usage:
12004 <pre><code>
12005 // slide the element horizontally to x position 200 while changing the height and opacity
12006 el.shift({ x: 200, height: 50, opacity: .8 });
12007
12008 // common config options shown with default values.
12009 el.shift({
12010     width: [element's width],
12011     height: [element's height],
12012     x: [element's x position],
12013     y: [element's y position],
12014     opacity: [element's opacity],
12015     easing: 'easeOut',
12016     duration: .35
12017 });
12018 </code></pre>
12019     * @param {Object} options  Object literal with any of the Fx config options
12020     * @return {Roo.Element} The Element
12021     */
12022     shift : function(o){
12023         var el = this.getFxEl();
12024         o = o || {};
12025         el.queueFx(o, function(){
12026             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12027             if(w !== undefined){
12028                 a.width = {to: this.adjustWidth(w)};
12029             }
12030             if(h !== undefined){
12031                 a.height = {to: this.adjustHeight(h)};
12032             }
12033             if(x !== undefined || y !== undefined){
12034                 a.points = {to: [
12035                     x !== undefined ? x : this.getX(),
12036                     y !== undefined ? y : this.getY()
12037                 ]};
12038             }
12039             if(op !== undefined){
12040                 a.opacity = {to: op};
12041             }
12042             if(o.xy !== undefined){
12043                 a.points = {to: o.xy};
12044             }
12045             arguments.callee.anim = this.fxanim(a,
12046                 o, 'motion', .35, "easeOut", function(){
12047                 el.afterFx(o);
12048             });
12049         });
12050         return this;
12051     },
12052
12053         /**
12054          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12055          * ending point of the effect.
12056          * Usage:
12057          *<pre><code>
12058 // default: slide the element downward while fading out
12059 el.ghost();
12060
12061 // custom: slide the element out to the right with a 2-second duration
12062 el.ghost('r', { duration: 2 });
12063
12064 // common config options shown with default values
12065 el.ghost('b', {
12066     easing: 'easeOut',
12067     duration: .5
12068     remove: false,
12069     useDisplay: false
12070 });
12071 </code></pre>
12072          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12073          * @param {Object} options (optional) Object literal with any of the Fx config options
12074          * @return {Roo.Element} The Element
12075          */
12076     ghost : function(anchor, o){
12077         var el = this.getFxEl();
12078         o = o || {};
12079
12080         el.queueFx(o, function(){
12081             anchor = anchor || "b";
12082
12083             // restore values after effect
12084             var r = this.getFxRestore();
12085             var w = this.getWidth(),
12086                 h = this.getHeight();
12087
12088             var st = this.dom.style;
12089
12090             var after = function(){
12091                 if(o.useDisplay){
12092                     el.setDisplayed(false);
12093                 }else{
12094                     el.hide();
12095                 }
12096
12097                 el.clearOpacity();
12098                 el.setPositioning(r.pos);
12099                 st.width = r.width;
12100                 st.height = r.height;
12101
12102                 el.afterFx(o);
12103             };
12104
12105             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12106             switch(anchor.toLowerCase()){
12107                 case "t":
12108                     pt.by = [0, -h];
12109                 break;
12110                 case "l":
12111                     pt.by = [-w, 0];
12112                 break;
12113                 case "r":
12114                     pt.by = [w, 0];
12115                 break;
12116                 case "b":
12117                     pt.by = [0, h];
12118                 break;
12119                 case "tl":
12120                     pt.by = [-w, -h];
12121                 break;
12122                 case "bl":
12123                     pt.by = [-w, h];
12124                 break;
12125                 case "br":
12126                     pt.by = [w, h];
12127                 break;
12128                 case "tr":
12129                     pt.by = [w, -h];
12130                 break;
12131             }
12132
12133             arguments.callee.anim = this.fxanim(a,
12134                 o,
12135                 'motion',
12136                 .5,
12137                 "easeOut", after);
12138         });
12139         return this;
12140     },
12141
12142         /**
12143          * Ensures that all effects queued after syncFx is called on the element are
12144          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12145          * @return {Roo.Element} The Element
12146          */
12147     syncFx : function(){
12148         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12149             block : false,
12150             concurrent : true,
12151             stopFx : false
12152         });
12153         return this;
12154     },
12155
12156         /**
12157          * Ensures that all effects queued after sequenceFx is called on the element are
12158          * run in sequence.  This is the opposite of {@link #syncFx}.
12159          * @return {Roo.Element} The Element
12160          */
12161     sequenceFx : function(){
12162         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12163             block : false,
12164             concurrent : false,
12165             stopFx : false
12166         });
12167         return this;
12168     },
12169
12170         /* @private */
12171     nextFx : function(){
12172         var ef = this.fxQueue[0];
12173         if(ef){
12174             ef.call(this);
12175         }
12176     },
12177
12178         /**
12179          * Returns true if the element has any effects actively running or queued, else returns false.
12180          * @return {Boolean} True if element has active effects, else false
12181          */
12182     hasActiveFx : function(){
12183         return this.fxQueue && this.fxQueue[0];
12184     },
12185
12186         /**
12187          * Stops any running effects and clears the element's internal effects queue if it contains
12188          * any additional effects that haven't started yet.
12189          * @return {Roo.Element} The Element
12190          */
12191     stopFx : function(){
12192         if(this.hasActiveFx()){
12193             var cur = this.fxQueue[0];
12194             if(cur && cur.anim && cur.anim.isAnimated()){
12195                 this.fxQueue = [cur]; // clear out others
12196                 cur.anim.stop(true);
12197             }
12198         }
12199         return this;
12200     },
12201
12202         /* @private */
12203     beforeFx : function(o){
12204         if(this.hasActiveFx() && !o.concurrent){
12205            if(o.stopFx){
12206                this.stopFx();
12207                return true;
12208            }
12209            return false;
12210         }
12211         return true;
12212     },
12213
12214         /**
12215          * Returns true if the element is currently blocking so that no other effect can be queued
12216          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12217          * used to ensure that an effect initiated by a user action runs to completion prior to the
12218          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12219          * @return {Boolean} True if blocking, else false
12220          */
12221     hasFxBlock : function(){
12222         var q = this.fxQueue;
12223         return q && q[0] && q[0].block;
12224     },
12225
12226         /* @private */
12227     queueFx : function(o, fn){
12228         if(!this.fxQueue){
12229             this.fxQueue = [];
12230         }
12231         if(!this.hasFxBlock()){
12232             Roo.applyIf(o, this.fxDefaults);
12233             if(!o.concurrent){
12234                 var run = this.beforeFx(o);
12235                 fn.block = o.block;
12236                 this.fxQueue.push(fn);
12237                 if(run){
12238                     this.nextFx();
12239                 }
12240             }else{
12241                 fn.call(this);
12242             }
12243         }
12244         return this;
12245     },
12246
12247         /* @private */
12248     fxWrap : function(pos, o, vis){
12249         var wrap;
12250         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12251             var wrapXY;
12252             if(o.fixPosition){
12253                 wrapXY = this.getXY();
12254             }
12255             var div = document.createElement("div");
12256             div.style.visibility = vis;
12257             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12258             wrap.setPositioning(pos);
12259             if(wrap.getStyle("position") == "static"){
12260                 wrap.position("relative");
12261             }
12262             this.clearPositioning('auto');
12263             wrap.clip();
12264             wrap.dom.appendChild(this.dom);
12265             if(wrapXY){
12266                 wrap.setXY(wrapXY);
12267             }
12268         }
12269         return wrap;
12270     },
12271
12272         /* @private */
12273     fxUnwrap : function(wrap, pos, o){
12274         this.clearPositioning();
12275         this.setPositioning(pos);
12276         if(!o.wrap){
12277             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12278             wrap.remove();
12279         }
12280     },
12281
12282         /* @private */
12283     getFxRestore : function(){
12284         var st = this.dom.style;
12285         return {pos: this.getPositioning(), width: st.width, height : st.height};
12286     },
12287
12288         /* @private */
12289     afterFx : function(o){
12290         if(o.afterStyle){
12291             this.applyStyles(o.afterStyle);
12292         }
12293         if(o.afterCls){
12294             this.addClass(o.afterCls);
12295         }
12296         if(o.remove === true){
12297             this.remove();
12298         }
12299         Roo.callback(o.callback, o.scope, [this]);
12300         if(!o.concurrent){
12301             this.fxQueue.shift();
12302             this.nextFx();
12303         }
12304     },
12305
12306         /* @private */
12307     getFxEl : function(){ // support for composite element fx
12308         return Roo.get(this.dom);
12309     },
12310
12311         /* @private */
12312     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12313         animType = animType || 'run';
12314         opt = opt || {};
12315         var anim = Roo.lib.Anim[animType](
12316             this.dom, args,
12317             (opt.duration || defaultDur) || .35,
12318             (opt.easing || defaultEase) || 'easeOut',
12319             function(){
12320                 Roo.callback(cb, this);
12321             },
12322             this
12323         );
12324         opt.anim = anim;
12325         return anim;
12326     }
12327 };
12328
12329 // backwords compat
12330 Roo.Fx.resize = Roo.Fx.scale;
12331
12332 //When included, Roo.Fx is automatically applied to Element so that all basic
12333 //effects are available directly via the Element API
12334 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12335  * Based on:
12336  * Ext JS Library 1.1.1
12337  * Copyright(c) 2006-2007, Ext JS, LLC.
12338  *
12339  * Originally Released Under LGPL - original licence link has changed is not relivant.
12340  *
12341  * Fork - LGPL
12342  * <script type="text/javascript">
12343  */
12344
12345
12346 /**
12347  * @class Roo.CompositeElement
12348  * Standard composite class. Creates a Roo.Element for every element in the collection.
12349  * <br><br>
12350  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12351  * actions will be performed on all the elements in this collection.</b>
12352  * <br><br>
12353  * All methods return <i>this</i> and can be chained.
12354  <pre><code>
12355  var els = Roo.select("#some-el div.some-class", true);
12356  // or select directly from an existing element
12357  var el = Roo.get('some-el');
12358  el.select('div.some-class', true);
12359
12360  els.setWidth(100); // all elements become 100 width
12361  els.hide(true); // all elements fade out and hide
12362  // or
12363  els.setWidth(100).hide(true);
12364  </code></pre>
12365  */
12366 Roo.CompositeElement = function(els){
12367     this.elements = [];
12368     this.addElements(els);
12369 };
12370 Roo.CompositeElement.prototype = {
12371     isComposite: true,
12372     addElements : function(els){
12373         if(!els) {
12374             return this;
12375         }
12376         if(typeof els == "string"){
12377             els = Roo.Element.selectorFunction(els);
12378         }
12379         var yels = this.elements;
12380         var index = yels.length-1;
12381         for(var i = 0, len = els.length; i < len; i++) {
12382                 yels[++index] = Roo.get(els[i]);
12383         }
12384         return this;
12385     },
12386
12387     /**
12388     * Clears this composite and adds the elements returned by the passed selector.
12389     * @param {String/Array} els A string CSS selector, an array of elements or an element
12390     * @return {CompositeElement} this
12391     */
12392     fill : function(els){
12393         this.elements = [];
12394         this.add(els);
12395         return this;
12396     },
12397
12398     /**
12399     * Filters this composite to only elements that match the passed selector.
12400     * @param {String} selector A string CSS selector
12401     * @param {Boolean} inverse return inverse filter (not matches)
12402     * @return {CompositeElement} this
12403     */
12404     filter : function(selector, inverse){
12405         var els = [];
12406         inverse = inverse || false;
12407         this.each(function(el){
12408             var match = inverse ? !el.is(selector) : el.is(selector);
12409             if(match){
12410                 els[els.length] = el.dom;
12411             }
12412         });
12413         this.fill(els);
12414         return this;
12415     },
12416
12417     invoke : function(fn, args){
12418         var els = this.elements;
12419         for(var i = 0, len = els.length; i < len; i++) {
12420                 Roo.Element.prototype[fn].apply(els[i], args);
12421         }
12422         return this;
12423     },
12424     /**
12425     * Adds elements to this composite.
12426     * @param {String/Array} els A string CSS selector, an array of elements or an element
12427     * @return {CompositeElement} this
12428     */
12429     add : function(els){
12430         if(typeof els == "string"){
12431             this.addElements(Roo.Element.selectorFunction(els));
12432         }else if(els.length !== undefined){
12433             this.addElements(els);
12434         }else{
12435             this.addElements([els]);
12436         }
12437         return this;
12438     },
12439     /**
12440     * Calls the passed function passing (el, this, index) for each element in this composite.
12441     * @param {Function} fn The function to call
12442     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12443     * @return {CompositeElement} this
12444     */
12445     each : function(fn, scope){
12446         var els = this.elements;
12447         for(var i = 0, len = els.length; i < len; i++){
12448             if(fn.call(scope || els[i], els[i], this, i) === false) {
12449                 break;
12450             }
12451         }
12452         return this;
12453     },
12454
12455     /**
12456      * Returns the Element object at the specified index
12457      * @param {Number} index
12458      * @return {Roo.Element}
12459      */
12460     item : function(index){
12461         return this.elements[index] || null;
12462     },
12463
12464     /**
12465      * Returns the first Element
12466      * @return {Roo.Element}
12467      */
12468     first : function(){
12469         return this.item(0);
12470     },
12471
12472     /**
12473      * Returns the last Element
12474      * @return {Roo.Element}
12475      */
12476     last : function(){
12477         return this.item(this.elements.length-1);
12478     },
12479
12480     /**
12481      * Returns the number of elements in this composite
12482      * @return Number
12483      */
12484     getCount : function(){
12485         return this.elements.length;
12486     },
12487
12488     /**
12489      * Returns true if this composite contains the passed element
12490      * @return Boolean
12491      */
12492     contains : function(el){
12493         return this.indexOf(el) !== -1;
12494     },
12495
12496     /**
12497      * Returns true if this composite contains the passed element
12498      * @return Boolean
12499      */
12500     indexOf : function(el){
12501         return this.elements.indexOf(Roo.get(el));
12502     },
12503
12504
12505     /**
12506     * Removes the specified element(s).
12507     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12508     * or an array of any of those.
12509     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12510     * @return {CompositeElement} this
12511     */
12512     removeElement : function(el, removeDom){
12513         if(el instanceof Array){
12514             for(var i = 0, len = el.length; i < len; i++){
12515                 this.removeElement(el[i]);
12516             }
12517             return this;
12518         }
12519         var index = typeof el == 'number' ? el : this.indexOf(el);
12520         if(index !== -1){
12521             if(removeDom){
12522                 var d = this.elements[index];
12523                 if(d.dom){
12524                     d.remove();
12525                 }else{
12526                     d.parentNode.removeChild(d);
12527                 }
12528             }
12529             this.elements.splice(index, 1);
12530         }
12531         return this;
12532     },
12533
12534     /**
12535     * Replaces the specified element with the passed element.
12536     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12537     * to replace.
12538     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12539     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12540     * @return {CompositeElement} this
12541     */
12542     replaceElement : function(el, replacement, domReplace){
12543         var index = typeof el == 'number' ? el : this.indexOf(el);
12544         if(index !== -1){
12545             if(domReplace){
12546                 this.elements[index].replaceWith(replacement);
12547             }else{
12548                 this.elements.splice(index, 1, Roo.get(replacement))
12549             }
12550         }
12551         return this;
12552     },
12553
12554     /**
12555      * Removes all elements.
12556      */
12557     clear : function(){
12558         this.elements = [];
12559     }
12560 };
12561 (function(){
12562     Roo.CompositeElement.createCall = function(proto, fnName){
12563         if(!proto[fnName]){
12564             proto[fnName] = function(){
12565                 return this.invoke(fnName, arguments);
12566             };
12567         }
12568     };
12569     for(var fnName in Roo.Element.prototype){
12570         if(typeof Roo.Element.prototype[fnName] == "function"){
12571             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12572         }
12573     };
12574 })();
12575 /*
12576  * Based on:
12577  * Ext JS Library 1.1.1
12578  * Copyright(c) 2006-2007, Ext JS, LLC.
12579  *
12580  * Originally Released Under LGPL - original licence link has changed is not relivant.
12581  *
12582  * Fork - LGPL
12583  * <script type="text/javascript">
12584  */
12585
12586 /**
12587  * @class Roo.CompositeElementLite
12588  * @extends Roo.CompositeElement
12589  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12590  <pre><code>
12591  var els = Roo.select("#some-el div.some-class");
12592  // or select directly from an existing element
12593  var el = Roo.get('some-el');
12594  el.select('div.some-class');
12595
12596  els.setWidth(100); // all elements become 100 width
12597  els.hide(true); // all elements fade out and hide
12598  // or
12599  els.setWidth(100).hide(true);
12600  </code></pre><br><br>
12601  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12602  * actions will be performed on all the elements in this collection.</b>
12603  */
12604 Roo.CompositeElementLite = function(els){
12605     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12606     this.el = new Roo.Element.Flyweight();
12607 };
12608 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12609     addElements : function(els){
12610         if(els){
12611             if(els instanceof Array){
12612                 this.elements = this.elements.concat(els);
12613             }else{
12614                 var yels = this.elements;
12615                 var index = yels.length-1;
12616                 for(var i = 0, len = els.length; i < len; i++) {
12617                     yels[++index] = els[i];
12618                 }
12619             }
12620         }
12621         return this;
12622     },
12623     invoke : function(fn, args){
12624         var els = this.elements;
12625         var el = this.el;
12626         for(var i = 0, len = els.length; i < len; i++) {
12627             el.dom = els[i];
12628                 Roo.Element.prototype[fn].apply(el, args);
12629         }
12630         return this;
12631     },
12632     /**
12633      * Returns a flyweight Element of the dom element object at the specified index
12634      * @param {Number} index
12635      * @return {Roo.Element}
12636      */
12637     item : function(index){
12638         if(!this.elements[index]){
12639             return null;
12640         }
12641         this.el.dom = this.elements[index];
12642         return this.el;
12643     },
12644
12645     // fixes scope with flyweight
12646     addListener : function(eventName, handler, scope, opt){
12647         var els = this.elements;
12648         for(var i = 0, len = els.length; i < len; i++) {
12649             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12650         }
12651         return this;
12652     },
12653
12654     /**
12655     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12656     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12657     * a reference to the dom node, use el.dom.</b>
12658     * @param {Function} fn The function to call
12659     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12660     * @return {CompositeElement} this
12661     */
12662     each : function(fn, scope){
12663         var els = this.elements;
12664         var el = this.el;
12665         for(var i = 0, len = els.length; i < len; i++){
12666             el.dom = els[i];
12667                 if(fn.call(scope || el, el, this, i) === false){
12668                 break;
12669             }
12670         }
12671         return this;
12672     },
12673
12674     indexOf : function(el){
12675         return this.elements.indexOf(Roo.getDom(el));
12676     },
12677
12678     replaceElement : function(el, replacement, domReplace){
12679         var index = typeof el == 'number' ? el : this.indexOf(el);
12680         if(index !== -1){
12681             replacement = Roo.getDom(replacement);
12682             if(domReplace){
12683                 var d = this.elements[index];
12684                 d.parentNode.insertBefore(replacement, d);
12685                 d.parentNode.removeChild(d);
12686             }
12687             this.elements.splice(index, 1, replacement);
12688         }
12689         return this;
12690     }
12691 });
12692 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12693
12694 /*
12695  * Based on:
12696  * Ext JS Library 1.1.1
12697  * Copyright(c) 2006-2007, Ext JS, LLC.
12698  *
12699  * Originally Released Under LGPL - original licence link has changed is not relivant.
12700  *
12701  * Fork - LGPL
12702  * <script type="text/javascript">
12703  */
12704
12705  
12706
12707 /**
12708  * @class Roo.data.Connection
12709  * @extends Roo.util.Observable
12710  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12711  * either to a configured URL, or to a URL specified at request time. 
12712  * 
12713  * Requests made by this class are asynchronous, and will return immediately. No data from
12714  * the server will be available to the statement immediately following the {@link #request} call.
12715  * To process returned data, use a callback in the request options object, or an event listener.
12716  * 
12717  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12718  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12719  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12720  * property and, if present, the IFRAME's XML document as the responseXML property.
12721  * 
12722  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12723  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12724  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12725  * standard DOM methods.
12726  * @constructor
12727  * @param {Object} config a configuration object.
12728  */
12729 Roo.data.Connection = function(config){
12730     Roo.apply(this, config);
12731     this.addEvents({
12732         /**
12733          * @event beforerequest
12734          * Fires before a network request is made to retrieve a data object.
12735          * @param {Connection} conn This Connection object.
12736          * @param {Object} options The options config object passed to the {@link #request} method.
12737          */
12738         "beforerequest" : true,
12739         /**
12740          * @event requestcomplete
12741          * Fires if the request was successfully completed.
12742          * @param {Connection} conn This Connection object.
12743          * @param {Object} response The XHR object containing the response data.
12744          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12745          * @param {Object} options The options config object passed to the {@link #request} method.
12746          */
12747         "requestcomplete" : true,
12748         /**
12749          * @event requestexception
12750          * Fires if an error HTTP status was returned from the server.
12751          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12752          * @param {Connection} conn This Connection object.
12753          * @param {Object} response The XHR object containing the response data.
12754          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12755          * @param {Object} options The options config object passed to the {@link #request} method.
12756          */
12757         "requestexception" : true
12758     });
12759     Roo.data.Connection.superclass.constructor.call(this);
12760 };
12761
12762 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12763     /**
12764      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12765      */
12766     /**
12767      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12768      * extra parameters to each request made by this object. (defaults to undefined)
12769      */
12770     /**
12771      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12772      *  to each request made by this object. (defaults to undefined)
12773      */
12774     /**
12775      * @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)
12776      */
12777     /**
12778      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12779      */
12780     timeout : 30000,
12781     /**
12782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12783      * @type Boolean
12784      */
12785     autoAbort:false,
12786
12787     /**
12788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12789      * @type Boolean
12790      */
12791     disableCaching: true,
12792
12793     /**
12794      * Sends an HTTP request to a remote server.
12795      * @param {Object} options An object which may contain the following properties:<ul>
12796      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12797      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12798      * request, a url encoded string or a function to call to get either.</li>
12799      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12800      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12801      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12802      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12803      * <li>options {Object} The parameter to the request call.</li>
12804      * <li>success {Boolean} True if the request succeeded.</li>
12805      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12806      * </ul></li>
12807      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12808      * The callback is passed the following parameters:<ul>
12809      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12810      * <li>options {Object} The parameter to the request call.</li>
12811      * </ul></li>
12812      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12813      * The callback is passed the following parameters:<ul>
12814      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12815      * <li>options {Object} The parameter to the request call.</li>
12816      * </ul></li>
12817      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12818      * for the callback function. Defaults to the browser window.</li>
12819      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12820      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12821      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12822      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12823      * params for the post data. Any params will be appended to the URL.</li>
12824      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12825      * </ul>
12826      * @return {Number} transactionId
12827      */
12828     request : function(o){
12829         if(this.fireEvent("beforerequest", this, o) !== false){
12830             var p = o.params;
12831
12832             if(typeof p == "function"){
12833                 p = p.call(o.scope||window, o);
12834             }
12835             if(typeof p == "object"){
12836                 p = Roo.urlEncode(o.params);
12837             }
12838             if(this.extraParams){
12839                 var extras = Roo.urlEncode(this.extraParams);
12840                 p = p ? (p + '&' + extras) : extras;
12841             }
12842
12843             var url = o.url || this.url;
12844             if(typeof url == 'function'){
12845                 url = url.call(o.scope||window, o);
12846             }
12847
12848             if(o.form){
12849                 var form = Roo.getDom(o.form);
12850                 url = url || form.action;
12851
12852                 var enctype = form.getAttribute("enctype");
12853                 
12854                 if (o.formData) {
12855                     return this.doFormDataUpload(o, url);
12856                 }
12857                 
12858                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12859                     return this.doFormUpload(o, p, url);
12860                 }
12861                 var f = Roo.lib.Ajax.serializeForm(form);
12862                 p = p ? (p + '&' + f) : f;
12863             }
12864             
12865             if (!o.form && o.formData) {
12866                 o.formData = o.formData === true ? new FormData() : o.formData;
12867                 for (var k in o.params) {
12868                     o.formData.append(k,o.params[k]);
12869                 }
12870                     
12871                 return this.doFormDataUpload(o, url);
12872             }
12873             
12874
12875             var hs = o.headers;
12876             if(this.defaultHeaders){
12877                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12878                 if(!o.headers){
12879                     o.headers = hs;
12880                 }
12881             }
12882
12883             var cb = {
12884                 success: this.handleResponse,
12885                 failure: this.handleFailure,
12886                 scope: this,
12887                 argument: {options: o},
12888                 timeout : o.timeout || this.timeout
12889             };
12890
12891             var method = o.method||this.method||(p ? "POST" : "GET");
12892
12893             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12894                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12895             }
12896
12897             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12898                 if(o.autoAbort){
12899                     this.abort();
12900                 }
12901             }else if(this.autoAbort !== false){
12902                 this.abort();
12903             }
12904
12905             if((method == 'GET' && p) || o.xmlData){
12906                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12907                 p = '';
12908             }
12909             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12910             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12911             Roo.lib.Ajax.useDefaultHeader == true;
12912             return this.transId;
12913         }else{
12914             Roo.callback(o.callback, o.scope, [o, null, null]);
12915             return null;
12916         }
12917     },
12918
12919     /**
12920      * Determine whether this object has a request outstanding.
12921      * @param {Number} transactionId (Optional) defaults to the last transaction
12922      * @return {Boolean} True if there is an outstanding request.
12923      */
12924     isLoading : function(transId){
12925         if(transId){
12926             return Roo.lib.Ajax.isCallInProgress(transId);
12927         }else{
12928             return this.transId ? true : false;
12929         }
12930     },
12931
12932     /**
12933      * Aborts any outstanding request.
12934      * @param {Number} transactionId (Optional) defaults to the last transaction
12935      */
12936     abort : function(transId){
12937         if(transId || this.isLoading()){
12938             Roo.lib.Ajax.abort(transId || this.transId);
12939         }
12940     },
12941
12942     // private
12943     handleResponse : function(response){
12944         this.transId = false;
12945         var options = response.argument.options;
12946         response.argument = options ? options.argument : null;
12947         this.fireEvent("requestcomplete", this, response, options);
12948         Roo.callback(options.success, options.scope, [response, options]);
12949         Roo.callback(options.callback, options.scope, [options, true, response]);
12950     },
12951
12952     // private
12953     handleFailure : function(response, e){
12954         this.transId = false;
12955         var options = response.argument.options;
12956         response.argument = options ? options.argument : null;
12957         this.fireEvent("requestexception", this, response, options, e);
12958         Roo.callback(options.failure, options.scope, [response, options]);
12959         Roo.callback(options.callback, options.scope, [options, false, response]);
12960     },
12961
12962     // private
12963     doFormUpload : function(o, ps, url){
12964         var id = Roo.id();
12965         var frame = document.createElement('iframe');
12966         frame.id = id;
12967         frame.name = id;
12968         frame.className = 'x-hidden';
12969         if(Roo.isIE){
12970             frame.src = Roo.SSL_SECURE_URL;
12971         }
12972         document.body.appendChild(frame);
12973
12974         if(Roo.isIE){
12975            document.frames[id].name = id;
12976         }
12977
12978         var form = Roo.getDom(o.form);
12979         form.target = id;
12980         form.method = 'POST';
12981         form.enctype = form.encoding = 'multipart/form-data';
12982         if(url){
12983             form.action = url;
12984         }
12985
12986         var hiddens, hd;
12987         if(ps){ // add dynamic params
12988             hiddens = [];
12989             ps = Roo.urlDecode(ps, false);
12990             for(var k in ps){
12991                 if(ps.hasOwnProperty(k)){
12992                     hd = document.createElement('input');
12993                     hd.type = 'hidden';
12994                     hd.name = k;
12995                     hd.value = ps[k];
12996                     form.appendChild(hd);
12997                     hiddens.push(hd);
12998                 }
12999             }
13000         }
13001
13002         function cb(){
13003             var r = {  // bogus response object
13004                 responseText : '',
13005                 responseXML : null
13006             };
13007
13008             r.argument = o ? o.argument : null;
13009
13010             try { //
13011                 var doc;
13012                 if(Roo.isIE){
13013                     doc = frame.contentWindow.document;
13014                 }else {
13015                     doc = (frame.contentDocument || window.frames[id].document);
13016                 }
13017                 if(doc && doc.body){
13018                     r.responseText = doc.body.innerHTML;
13019                 }
13020                 if(doc && doc.XMLDocument){
13021                     r.responseXML = doc.XMLDocument;
13022                 }else {
13023                     r.responseXML = doc;
13024                 }
13025             }
13026             catch(e) {
13027                 // ignore
13028             }
13029
13030             Roo.EventManager.removeListener(frame, 'load', cb, this);
13031
13032             this.fireEvent("requestcomplete", this, r, o);
13033             Roo.callback(o.success, o.scope, [r, o]);
13034             Roo.callback(o.callback, o.scope, [o, true, r]);
13035
13036             setTimeout(function(){document.body.removeChild(frame);}, 100);
13037         }
13038
13039         Roo.EventManager.on(frame, 'load', cb, this);
13040         form.submit();
13041
13042         if(hiddens){ // remove dynamic params
13043             for(var i = 0, len = hiddens.length; i < len; i++){
13044                 form.removeChild(hiddens[i]);
13045             }
13046         }
13047     },
13048     // this is a 'formdata version???'
13049     
13050     
13051     doFormDataUpload : function(o,  url)
13052     {
13053         var formData;
13054         if (o.form) {
13055             var form =  Roo.getDom(o.form);
13056             form.enctype = form.encoding = 'multipart/form-data';
13057             formData = o.formData === true ? new FormData(form) : o.formData;
13058         } else {
13059             formData = o.formData === true ? new FormData() : o.formData;
13060         }
13061         
13062       
13063         var cb = {
13064             success: this.handleResponse,
13065             failure: this.handleFailure,
13066             scope: this,
13067             argument: {options: o},
13068             timeout : o.timeout || this.timeout
13069         };
13070  
13071         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13072             if(o.autoAbort){
13073                 this.abort();
13074             }
13075         }else if(this.autoAbort !== false){
13076             this.abort();
13077         }
13078
13079         //Roo.lib.Ajax.defaultPostHeader = null;
13080         Roo.lib.Ajax.useDefaultHeader = false;
13081         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13082         Roo.lib.Ajax.useDefaultHeader = true;
13083  
13084          
13085     }
13086     
13087 });
13088 /*
13089  * Based on:
13090  * Ext JS Library 1.1.1
13091  * Copyright(c) 2006-2007, Ext JS, LLC.
13092  *
13093  * Originally Released Under LGPL - original licence link has changed is not relivant.
13094  *
13095  * Fork - LGPL
13096  * <script type="text/javascript">
13097  */
13098  
13099 /**
13100  * Global Ajax request class.
13101  * 
13102  * @class Roo.Ajax
13103  * @extends Roo.data.Connection
13104  * @static
13105  * 
13106  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13107  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13108  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13109  * @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)
13110  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13111  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13112  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13113  */
13114 Roo.Ajax = new Roo.data.Connection({
13115     // fix up the docs
13116     /**
13117      * @scope Roo.Ajax
13118      * @type {Boolear} 
13119      */
13120     autoAbort : false,
13121
13122     /**
13123      * Serialize the passed form into a url encoded string
13124      * @scope Roo.Ajax
13125      * @param {String/HTMLElement} form
13126      * @return {String}
13127      */
13128     serializeForm : function(form){
13129         return Roo.lib.Ajax.serializeForm(form);
13130     }
13131 });/*
13132  * Based on:
13133  * Ext JS Library 1.1.1
13134  * Copyright(c) 2006-2007, Ext JS, LLC.
13135  *
13136  * Originally Released Under LGPL - original licence link has changed is not relivant.
13137  *
13138  * Fork - LGPL
13139  * <script type="text/javascript">
13140  */
13141
13142  
13143 /**
13144  * @class Roo.UpdateManager
13145  * @extends Roo.util.Observable
13146  * Provides AJAX-style update for Element object.<br><br>
13147  * Usage:<br>
13148  * <pre><code>
13149  * // Get it from a Roo.Element object
13150  * var el = Roo.get("foo");
13151  * var mgr = el.getUpdateManager();
13152  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13153  * ...
13154  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13155  * <br>
13156  * // or directly (returns the same UpdateManager instance)
13157  * var mgr = new Roo.UpdateManager("myElementId");
13158  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13159  * mgr.on("update", myFcnNeedsToKnow);
13160  * <br>
13161    // short handed call directly from the element object
13162    Roo.get("foo").load({
13163         url: "bar.php",
13164         scripts:true,
13165         params: "for=bar",
13166         text: "Loading Foo..."
13167    });
13168  * </code></pre>
13169  * @constructor
13170  * Create new UpdateManager directly.
13171  * @param {String/HTMLElement/Roo.Element} el The element to update
13172  * @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).
13173  */
13174 Roo.UpdateManager = function(el, forceNew){
13175     el = Roo.get(el);
13176     if(!forceNew && el.updateManager){
13177         return el.updateManager;
13178     }
13179     /**
13180      * The Element object
13181      * @type Roo.Element
13182      */
13183     this.el = el;
13184     /**
13185      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13186      * @type String
13187      */
13188     this.defaultUrl = null;
13189
13190     this.addEvents({
13191         /**
13192          * @event beforeupdate
13193          * Fired before an update is made, return false from your handler and the update is cancelled.
13194          * @param {Roo.Element} el
13195          * @param {String/Object/Function} url
13196          * @param {String/Object} params
13197          */
13198         "beforeupdate": true,
13199         /**
13200          * @event update
13201          * Fired after successful update is made.
13202          * @param {Roo.Element} el
13203          * @param {Object} oResponseObject The response Object
13204          */
13205         "update": true,
13206         /**
13207          * @event failure
13208          * Fired on update failure.
13209          * @param {Roo.Element} el
13210          * @param {Object} oResponseObject The response Object
13211          */
13212         "failure": true
13213     });
13214     var d = Roo.UpdateManager.defaults;
13215     /**
13216      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13217      * @type String
13218      */
13219     this.sslBlankUrl = d.sslBlankUrl;
13220     /**
13221      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13222      * @type Boolean
13223      */
13224     this.disableCaching = d.disableCaching;
13225     /**
13226      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13227      * @type String
13228      */
13229     this.indicatorText = d.indicatorText;
13230     /**
13231      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13232      * @type String
13233      */
13234     this.showLoadIndicator = d.showLoadIndicator;
13235     /**
13236      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13237      * @type Number
13238      */
13239     this.timeout = d.timeout;
13240
13241     /**
13242      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13243      * @type Boolean
13244      */
13245     this.loadScripts = d.loadScripts;
13246
13247     /**
13248      * Transaction object of current executing transaction
13249      */
13250     this.transaction = null;
13251
13252     /**
13253      * @private
13254      */
13255     this.autoRefreshProcId = null;
13256     /**
13257      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13258      * @type Function
13259      */
13260     this.refreshDelegate = this.refresh.createDelegate(this);
13261     /**
13262      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13263      * @type Function
13264      */
13265     this.updateDelegate = this.update.createDelegate(this);
13266     /**
13267      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13268      * @type Function
13269      */
13270     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13271     /**
13272      * @private
13273      */
13274     this.successDelegate = this.processSuccess.createDelegate(this);
13275     /**
13276      * @private
13277      */
13278     this.failureDelegate = this.processFailure.createDelegate(this);
13279
13280     if(!this.renderer){
13281      /**
13282       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13283       */
13284     this.renderer = new Roo.UpdateManager.BasicRenderer();
13285     }
13286     
13287     Roo.UpdateManager.superclass.constructor.call(this);
13288 };
13289
13290 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13291     /**
13292      * Get the Element this UpdateManager is bound to
13293      * @return {Roo.Element} The element
13294      */
13295     getEl : function(){
13296         return this.el;
13297     },
13298     /**
13299      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13300      * @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:
13301 <pre><code>
13302 um.update({<br/>
13303     url: "your-url.php",<br/>
13304     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13305     callback: yourFunction,<br/>
13306     scope: yourObject, //(optional scope)  <br/>
13307     discardUrl: false, <br/>
13308     nocache: false,<br/>
13309     text: "Loading...",<br/>
13310     timeout: 30,<br/>
13311     scripts: false<br/>
13312 });
13313 </code></pre>
13314      * The only required property is url. The optional properties nocache, text and scripts
13315      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13316      * @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}
13317      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13318      * @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.
13319      */
13320     update : function(url, params, callback, discardUrl){
13321         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13322             var method = this.method,
13323                 cfg;
13324             if(typeof url == "object"){ // must be config object
13325                 cfg = url;
13326                 url = cfg.url;
13327                 params = params || cfg.params;
13328                 callback = callback || cfg.callback;
13329                 discardUrl = discardUrl || cfg.discardUrl;
13330                 if(callback && cfg.scope){
13331                     callback = callback.createDelegate(cfg.scope);
13332                 }
13333                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13334                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13335                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13336                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13337                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13338             }
13339             this.showLoading();
13340             if(!discardUrl){
13341                 this.defaultUrl = url;
13342             }
13343             if(typeof url == "function"){
13344                 url = url.call(this);
13345             }
13346
13347             method = method || (params ? "POST" : "GET");
13348             if(method == "GET"){
13349                 url = this.prepareUrl(url);
13350             }
13351
13352             var o = Roo.apply(cfg ||{}, {
13353                 url : url,
13354                 params: params,
13355                 success: this.successDelegate,
13356                 failure: this.failureDelegate,
13357                 callback: undefined,
13358                 timeout: (this.timeout*1000),
13359                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13360             });
13361             Roo.log("updated manager called with timeout of " + o.timeout);
13362             this.transaction = Roo.Ajax.request(o);
13363         }
13364     },
13365
13366     /**
13367      * 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.
13368      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13369      * @param {String/HTMLElement} form The form Id or form element
13370      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13371      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13372      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13373      */
13374     formUpdate : function(form, url, reset, callback){
13375         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13376             if(typeof url == "function"){
13377                 url = url.call(this);
13378             }
13379             form = Roo.getDom(form);
13380             this.transaction = Roo.Ajax.request({
13381                 form: form,
13382                 url:url,
13383                 success: this.successDelegate,
13384                 failure: this.failureDelegate,
13385                 timeout: (this.timeout*1000),
13386                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13387             });
13388             this.showLoading.defer(1, this);
13389         }
13390     },
13391
13392     /**
13393      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13394      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13395      */
13396     refresh : function(callback){
13397         if(this.defaultUrl == null){
13398             return;
13399         }
13400         this.update(this.defaultUrl, null, callback, true);
13401     },
13402
13403     /**
13404      * Set this element to auto refresh.
13405      * @param {Number} interval How often to update (in seconds).
13406      * @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)
13407      * @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}
13408      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13409      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13410      */
13411     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13412         if(refreshNow){
13413             this.update(url || this.defaultUrl, params, callback, true);
13414         }
13415         if(this.autoRefreshProcId){
13416             clearInterval(this.autoRefreshProcId);
13417         }
13418         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13419     },
13420
13421     /**
13422      * Stop auto refresh on this element.
13423      */
13424      stopAutoRefresh : function(){
13425         if(this.autoRefreshProcId){
13426             clearInterval(this.autoRefreshProcId);
13427             delete this.autoRefreshProcId;
13428         }
13429     },
13430
13431     isAutoRefreshing : function(){
13432        return this.autoRefreshProcId ? true : false;
13433     },
13434     /**
13435      * Called to update the element to "Loading" state. Override to perform custom action.
13436      */
13437     showLoading : function(){
13438         if(this.showLoadIndicator){
13439             this.el.update(this.indicatorText);
13440         }
13441     },
13442
13443     /**
13444      * Adds unique parameter to query string if disableCaching = true
13445      * @private
13446      */
13447     prepareUrl : function(url){
13448         if(this.disableCaching){
13449             var append = "_dc=" + (new Date().getTime());
13450             if(url.indexOf("?") !== -1){
13451                 url += "&" + append;
13452             }else{
13453                 url += "?" + append;
13454             }
13455         }
13456         return url;
13457     },
13458
13459     /**
13460      * @private
13461      */
13462     processSuccess : function(response){
13463         this.transaction = null;
13464         if(response.argument.form && response.argument.reset){
13465             try{ // put in try/catch since some older FF releases had problems with this
13466                 response.argument.form.reset();
13467             }catch(e){}
13468         }
13469         if(this.loadScripts){
13470             this.renderer.render(this.el, response, this,
13471                 this.updateComplete.createDelegate(this, [response]));
13472         }else{
13473             this.renderer.render(this.el, response, this);
13474             this.updateComplete(response);
13475         }
13476     },
13477
13478     updateComplete : function(response){
13479         this.fireEvent("update", this.el, response);
13480         if(typeof response.argument.callback == "function"){
13481             response.argument.callback(this.el, true, response);
13482         }
13483     },
13484
13485     /**
13486      * @private
13487      */
13488     processFailure : function(response){
13489         this.transaction = null;
13490         this.fireEvent("failure", this.el, response);
13491         if(typeof response.argument.callback == "function"){
13492             response.argument.callback(this.el, false, response);
13493         }
13494     },
13495
13496     /**
13497      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13498      * @param {Object} renderer The object implementing the render() method
13499      */
13500     setRenderer : function(renderer){
13501         this.renderer = renderer;
13502     },
13503
13504     getRenderer : function(){
13505        return this.renderer;
13506     },
13507
13508     /**
13509      * Set the defaultUrl used for updates
13510      * @param {String/Function} defaultUrl The url or a function to call to get the url
13511      */
13512     setDefaultUrl : function(defaultUrl){
13513         this.defaultUrl = defaultUrl;
13514     },
13515
13516     /**
13517      * Aborts the executing transaction
13518      */
13519     abort : function(){
13520         if(this.transaction){
13521             Roo.Ajax.abort(this.transaction);
13522         }
13523     },
13524
13525     /**
13526      * Returns true if an update is in progress
13527      * @return {Boolean}
13528      */
13529     isUpdating : function(){
13530         if(this.transaction){
13531             return Roo.Ajax.isLoading(this.transaction);
13532         }
13533         return false;
13534     }
13535 });
13536
13537 /**
13538  * @class Roo.UpdateManager.defaults
13539  * @static (not really - but it helps the doc tool)
13540  * The defaults collection enables customizing the default properties of UpdateManager
13541  */
13542    Roo.UpdateManager.defaults = {
13543        /**
13544          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13545          * @type Number
13546          */
13547          timeout : 30,
13548
13549          /**
13550          * True to process scripts by default (Defaults to false).
13551          * @type Boolean
13552          */
13553         loadScripts : false,
13554
13555         /**
13556         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13557         * @type String
13558         */
13559         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13560         /**
13561          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13562          * @type Boolean
13563          */
13564         disableCaching : false,
13565         /**
13566          * Whether to show indicatorText when loading (Defaults to true).
13567          * @type Boolean
13568          */
13569         showLoadIndicator : true,
13570         /**
13571          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13572          * @type String
13573          */
13574         indicatorText : '<div class="loading-indicator">Loading...</div>'
13575    };
13576
13577 /**
13578  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13579  *Usage:
13580  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13581  * @param {String/HTMLElement/Roo.Element} el The element to update
13582  * @param {String} url The url
13583  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13584  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13585  * @static
13586  * @deprecated
13587  * @member Roo.UpdateManager
13588  */
13589 Roo.UpdateManager.updateElement = function(el, url, params, options){
13590     var um = Roo.get(el, true).getUpdateManager();
13591     Roo.apply(um, options);
13592     um.update(url, params, options ? options.callback : null);
13593 };
13594 // alias for backwards compat
13595 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13596 /**
13597  * @class Roo.UpdateManager.BasicRenderer
13598  * Default Content renderer. Updates the elements innerHTML with the responseText.
13599  */
13600 Roo.UpdateManager.BasicRenderer = function(){};
13601
13602 Roo.UpdateManager.BasicRenderer.prototype = {
13603     /**
13604      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13605      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13606      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13607      * @param {Roo.Element} el The element being rendered
13608      * @param {Object} response The YUI Connect response object
13609      * @param {UpdateManager} updateManager The calling update manager
13610      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13611      */
13612      render : function(el, response, updateManager, callback){
13613         el.update(response.responseText, updateManager.loadScripts, callback);
13614     }
13615 };
13616 /*
13617  * Based on:
13618  * Roo JS
13619  * (c)) Alan Knowles
13620  * Licence : LGPL
13621  */
13622
13623
13624 /**
13625  * @class Roo.DomTemplate
13626  * @extends Roo.Template
13627  * An effort at a dom based template engine..
13628  *
13629  * Similar to XTemplate, except it uses dom parsing to create the template..
13630  *
13631  * Supported features:
13632  *
13633  *  Tags:
13634
13635 <pre><code>
13636       {a_variable} - output encoded.
13637       {a_variable.format:("Y-m-d")} - call a method on the variable
13638       {a_variable:raw} - unencoded output
13639       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13640       {a_variable:this.method_on_template(...)} - call a method on the template object.
13641  
13642 </code></pre>
13643  *  The tpl tag:
13644 <pre><code>
13645         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13646         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13647         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13648         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13649   
13650 </code></pre>
13651  *      
13652  */
13653 Roo.DomTemplate = function()
13654 {
13655      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13656      if (this.html) {
13657         this.compile();
13658      }
13659 };
13660
13661
13662 Roo.extend(Roo.DomTemplate, Roo.Template, {
13663     /**
13664      * id counter for sub templates.
13665      */
13666     id : 0,
13667     /**
13668      * flag to indicate if dom parser is inside a pre,
13669      * it will strip whitespace if not.
13670      */
13671     inPre : false,
13672     
13673     /**
13674      * The various sub templates
13675      */
13676     tpls : false,
13677     
13678     
13679     
13680     /**
13681      *
13682      * basic tag replacing syntax
13683      * WORD:WORD()
13684      *
13685      * // you can fake an object call by doing this
13686      *  x.t:(test,tesT) 
13687      * 
13688      */
13689     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13690     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13691     
13692     iterChild : function (node, method) {
13693         
13694         var oldPre = this.inPre;
13695         if (node.tagName == 'PRE') {
13696             this.inPre = true;
13697         }
13698         for( var i = 0; i < node.childNodes.length; i++) {
13699             method.call(this, node.childNodes[i]);
13700         }
13701         this.inPre = oldPre;
13702     },
13703     
13704     
13705     
13706     /**
13707      * compile the template
13708      *
13709      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13710      *
13711      */
13712     compile: function()
13713     {
13714         var s = this.html;
13715         
13716         // covert the html into DOM...
13717         var doc = false;
13718         var div =false;
13719         try {
13720             doc = document.implementation.createHTMLDocument("");
13721             doc.documentElement.innerHTML =   this.html  ;
13722             div = doc.documentElement;
13723         } catch (e) {
13724             // old IE... - nasty -- it causes all sorts of issues.. with
13725             // images getting pulled from server..
13726             div = document.createElement('div');
13727             div.innerHTML = this.html;
13728         }
13729         //doc.documentElement.innerHTML = htmlBody
13730          
13731         
13732         
13733         this.tpls = [];
13734         var _t = this;
13735         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13736         
13737         var tpls = this.tpls;
13738         
13739         // create a top level template from the snippet..
13740         
13741         //Roo.log(div.innerHTML);
13742         
13743         var tpl = {
13744             uid : 'master',
13745             id : this.id++,
13746             attr : false,
13747             value : false,
13748             body : div.innerHTML,
13749             
13750             forCall : false,
13751             execCall : false,
13752             dom : div,
13753             isTop : true
13754             
13755         };
13756         tpls.unshift(tpl);
13757         
13758         
13759         // compile them...
13760         this.tpls = [];
13761         Roo.each(tpls, function(tp){
13762             this.compileTpl(tp);
13763             this.tpls[tp.id] = tp;
13764         }, this);
13765         
13766         this.master = tpls[0];
13767         return this;
13768         
13769         
13770     },
13771     
13772     compileNode : function(node, istop) {
13773         // test for
13774         //Roo.log(node);
13775         
13776         
13777         // skip anything not a tag..
13778         if (node.nodeType != 1) {
13779             if (node.nodeType == 3 && !this.inPre) {
13780                 // reduce white space..
13781                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13782                 
13783             }
13784             return;
13785         }
13786         
13787         var tpl = {
13788             uid : false,
13789             id : false,
13790             attr : false,
13791             value : false,
13792             body : '',
13793             
13794             forCall : false,
13795             execCall : false,
13796             dom : false,
13797             isTop : istop
13798             
13799             
13800         };
13801         
13802         
13803         switch(true) {
13804             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13805             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13806             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13807             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13808             // no default..
13809         }
13810         
13811         
13812         if (!tpl.attr) {
13813             // just itterate children..
13814             this.iterChild(node,this.compileNode);
13815             return;
13816         }
13817         tpl.uid = this.id++;
13818         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13819         node.removeAttribute('roo-'+ tpl.attr);
13820         if (tpl.attr != 'name') {
13821             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13822             node.parentNode.replaceChild(placeholder,  node);
13823         } else {
13824             
13825             var placeholder =  document.createElement('span');
13826             placeholder.className = 'roo-tpl-' + tpl.value;
13827             node.parentNode.replaceChild(placeholder,  node);
13828         }
13829         
13830         // parent now sees '{domtplXXXX}
13831         this.iterChild(node,this.compileNode);
13832         
13833         // we should now have node body...
13834         var div = document.createElement('div');
13835         div.appendChild(node);
13836         tpl.dom = node;
13837         // this has the unfortunate side effect of converting tagged attributes
13838         // eg. href="{...}" into %7C...%7D
13839         // this has been fixed by searching for those combo's although it's a bit hacky..
13840         
13841         
13842         tpl.body = div.innerHTML;
13843         
13844         
13845          
13846         tpl.id = tpl.uid;
13847         switch(tpl.attr) {
13848             case 'for' :
13849                 switch (tpl.value) {
13850                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13851                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13852                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13853                 }
13854                 break;
13855             
13856             case 'exec':
13857                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13858                 break;
13859             
13860             case 'if':     
13861                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13862                 break;
13863             
13864             case 'name':
13865                 tpl.id  = tpl.value; // replace non characters???
13866                 break;
13867             
13868         }
13869         
13870         
13871         this.tpls.push(tpl);
13872         
13873         
13874         
13875     },
13876     
13877     
13878     
13879     
13880     /**
13881      * Compile a segment of the template into a 'sub-template'
13882      *
13883      * 
13884      * 
13885      *
13886      */
13887     compileTpl : function(tpl)
13888     {
13889         var fm = Roo.util.Format;
13890         var useF = this.disableFormats !== true;
13891         
13892         var sep = Roo.isGecko ? "+\n" : ",\n";
13893         
13894         var undef = function(str) {
13895             Roo.debug && Roo.log("Property not found :"  + str);
13896             return '';
13897         };
13898           
13899         //Roo.log(tpl.body);
13900         
13901         
13902         
13903         var fn = function(m, lbrace, name, format, args)
13904         {
13905             //Roo.log("ARGS");
13906             //Roo.log(arguments);
13907             args = args ? args.replace(/\\'/g,"'") : args;
13908             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13909             if (typeof(format) == 'undefined') {
13910                 format =  'htmlEncode'; 
13911             }
13912             if (format == 'raw' ) {
13913                 format = false;
13914             }
13915             
13916             if(name.substr(0, 6) == 'domtpl'){
13917                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13918             }
13919             
13920             // build an array of options to determine if value is undefined..
13921             
13922             // basically get 'xxxx.yyyy' then do
13923             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13924             //    (function () { Roo.log("Property not found"); return ''; })() :
13925             //    ......
13926             
13927             var udef_ar = [];
13928             var lookfor = '';
13929             Roo.each(name.split('.'), function(st) {
13930                 lookfor += (lookfor.length ? '.': '') + st;
13931                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13932             });
13933             
13934             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13935             
13936             
13937             if(format && useF){
13938                 
13939                 args = args ? ',' + args : "";
13940                  
13941                 if(format.substr(0, 5) != "this."){
13942                     format = "fm." + format + '(';
13943                 }else{
13944                     format = 'this.call("'+ format.substr(5) + '", ';
13945                     args = ", values";
13946                 }
13947                 
13948                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13949             }
13950              
13951             if (args && args.length) {
13952                 // called with xxyx.yuu:(test,test)
13953                 // change to ()
13954                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13955             }
13956             // raw.. - :raw modifier..
13957             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13958             
13959         };
13960         var body;
13961         // branched to use + in gecko and [].join() in others
13962         if(Roo.isGecko){
13963             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13964                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13965                     "';};};";
13966         }else{
13967             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13968             body.push(tpl.body.replace(/(\r\n|\n)/g,
13969                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13970             body.push("'].join('');};};");
13971             body = body.join('');
13972         }
13973         
13974         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13975        
13976         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13977         eval(body);
13978         
13979         return this;
13980     },
13981      
13982     /**
13983      * same as applyTemplate, except it's done to one of the subTemplates
13984      * when using named templates, you can do:
13985      *
13986      * var str = pl.applySubTemplate('your-name', values);
13987      *
13988      * 
13989      * @param {Number} id of the template
13990      * @param {Object} values to apply to template
13991      * @param {Object} parent (normaly the instance of this object)
13992      */
13993     applySubTemplate : function(id, values, parent)
13994     {
13995         
13996         
13997         var t = this.tpls[id];
13998         
13999         
14000         try { 
14001             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14002                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14003                 return '';
14004             }
14005         } catch(e) {
14006             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14007             Roo.log(values);
14008           
14009             return '';
14010         }
14011         try { 
14012             
14013             if(t.execCall && t.execCall.call(this, values, parent)){
14014                 return '';
14015             }
14016         } catch(e) {
14017             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14018             Roo.log(values);
14019             return '';
14020         }
14021         
14022         try {
14023             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14024             parent = t.target ? values : parent;
14025             if(t.forCall && vs instanceof Array){
14026                 var buf = [];
14027                 for(var i = 0, len = vs.length; i < len; i++){
14028                     try {
14029                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14030                     } catch (e) {
14031                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14032                         Roo.log(e.body);
14033                         //Roo.log(t.compiled);
14034                         Roo.log(vs[i]);
14035                     }   
14036                 }
14037                 return buf.join('');
14038             }
14039         } catch (e) {
14040             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14041             Roo.log(values);
14042             return '';
14043         }
14044         try {
14045             return t.compiled.call(this, vs, parent);
14046         } catch (e) {
14047             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14048             Roo.log(e.body);
14049             //Roo.log(t.compiled);
14050             Roo.log(values);
14051             return '';
14052         }
14053     },
14054
14055    
14056
14057     applyTemplate : function(values){
14058         return this.master.compiled.call(this, values, {});
14059         //var s = this.subs;
14060     },
14061
14062     apply : function(){
14063         return this.applyTemplate.apply(this, arguments);
14064     }
14065
14066  });
14067
14068 Roo.DomTemplate.from = function(el){
14069     el = Roo.getDom(el);
14070     return new Roo.Domtemplate(el.value || el.innerHTML);
14071 };/*
14072  * Based on:
14073  * Ext JS Library 1.1.1
14074  * Copyright(c) 2006-2007, Ext JS, LLC.
14075  *
14076  * Originally Released Under LGPL - original licence link has changed is not relivant.
14077  *
14078  * Fork - LGPL
14079  * <script type="text/javascript">
14080  */
14081
14082 /**
14083  * @class Roo.util.DelayedTask
14084  * Provides a convenient method of performing setTimeout where a new
14085  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14086  * You can use this class to buffer
14087  * the keypress events for a certain number of milliseconds, and perform only if they stop
14088  * for that amount of time.
14089  * @constructor The parameters to this constructor serve as defaults and are not required.
14090  * @param {Function} fn (optional) The default function to timeout
14091  * @param {Object} scope (optional) The default scope of that timeout
14092  * @param {Array} args (optional) The default Array of arguments
14093  */
14094 Roo.util.DelayedTask = function(fn, scope, args){
14095     var id = null, d, t;
14096
14097     var call = function(){
14098         var now = new Date().getTime();
14099         if(now - t >= d){
14100             clearInterval(id);
14101             id = null;
14102             fn.apply(scope, args || []);
14103         }
14104     };
14105     /**
14106      * Cancels any pending timeout and queues a new one
14107      * @param {Number} delay The milliseconds to delay
14108      * @param {Function} newFn (optional) Overrides function passed to constructor
14109      * @param {Object} newScope (optional) Overrides scope passed to constructor
14110      * @param {Array} newArgs (optional) Overrides args passed to constructor
14111      */
14112     this.delay = function(delay, newFn, newScope, newArgs){
14113         if(id && delay != d){
14114             this.cancel();
14115         }
14116         d = delay;
14117         t = new Date().getTime();
14118         fn = newFn || fn;
14119         scope = newScope || scope;
14120         args = newArgs || args;
14121         if(!id){
14122             id = setInterval(call, d);
14123         }
14124     };
14125
14126     /**
14127      * Cancel the last queued timeout
14128      */
14129     this.cancel = function(){
14130         if(id){
14131             clearInterval(id);
14132             id = null;
14133         }
14134     };
14135 };/*
14136  * Based on:
14137  * Ext JS Library 1.1.1
14138  * Copyright(c) 2006-2007, Ext JS, LLC.
14139  *
14140  * Originally Released Under LGPL - original licence link has changed is not relivant.
14141  *
14142  * Fork - LGPL
14143  * <script type="text/javascript">
14144  */
14145 /**
14146  * @class Roo.util.TaskRunner
14147  * Manage background tasks - not sure why this is better that setInterval?
14148  * @static
14149  *
14150  */
14151  
14152 Roo.util.TaskRunner = function(interval){
14153     interval = interval || 10;
14154     var tasks = [], removeQueue = [];
14155     var id = 0;
14156     var running = false;
14157
14158     var stopThread = function(){
14159         running = false;
14160         clearInterval(id);
14161         id = 0;
14162     };
14163
14164     var startThread = function(){
14165         if(!running){
14166             running = true;
14167             id = setInterval(runTasks, interval);
14168         }
14169     };
14170
14171     var removeTask = function(task){
14172         removeQueue.push(task);
14173         if(task.onStop){
14174             task.onStop();
14175         }
14176     };
14177
14178     var runTasks = function(){
14179         if(removeQueue.length > 0){
14180             for(var i = 0, len = removeQueue.length; i < len; i++){
14181                 tasks.remove(removeQueue[i]);
14182             }
14183             removeQueue = [];
14184             if(tasks.length < 1){
14185                 stopThread();
14186                 return;
14187             }
14188         }
14189         var now = new Date().getTime();
14190         for(var i = 0, len = tasks.length; i < len; ++i){
14191             var t = tasks[i];
14192             var itime = now - t.taskRunTime;
14193             if(t.interval <= itime){
14194                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14195                 t.taskRunTime = now;
14196                 if(rt === false || t.taskRunCount === t.repeat){
14197                     removeTask(t);
14198                     return;
14199                 }
14200             }
14201             if(t.duration && t.duration <= (now - t.taskStartTime)){
14202                 removeTask(t);
14203             }
14204         }
14205     };
14206
14207     /**
14208      * Queues a new task.
14209      * @param {Object} task
14210      *
14211      * Task property : interval = how frequent to run.
14212      * Task object should implement
14213      * function run()
14214      * Task object may implement
14215      * function onStop()
14216      */
14217     this.start = function(task){
14218         tasks.push(task);
14219         task.taskStartTime = new Date().getTime();
14220         task.taskRunTime = 0;
14221         task.taskRunCount = 0;
14222         startThread();
14223         return task;
14224     };
14225     /**
14226      * Stop  new task.
14227      * @param {Object} task
14228      */
14229     this.stop = function(task){
14230         removeTask(task);
14231         return task;
14232     };
14233     /**
14234      * Stop all Tasks
14235      */
14236     this.stopAll = function(){
14237         stopThread();
14238         for(var i = 0, len = tasks.length; i < len; i++){
14239             if(tasks[i].onStop){
14240                 tasks[i].onStop();
14241             }
14242         }
14243         tasks = [];
14244         removeQueue = [];
14245     };
14246 };
14247
14248 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14249  * Based on:
14250  * Ext JS Library 1.1.1
14251  * Copyright(c) 2006-2007, Ext JS, LLC.
14252  *
14253  * Originally Released Under LGPL - original licence link has changed is not relivant.
14254  *
14255  * Fork - LGPL
14256  * <script type="text/javascript">
14257  */
14258
14259  
14260 /**
14261  * @class Roo.util.MixedCollection
14262  * @extends Roo.util.Observable
14263  * A Collection class that maintains both numeric indexes and keys and exposes events.
14264  * @constructor
14265  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14266  * collection (defaults to false)
14267  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14268  * and return the key value for that item.  This is used when available to look up the key on items that
14269  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14270  * equivalent to providing an implementation for the {@link #getKey} method.
14271  */
14272 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14273     this.items = [];
14274     this.map = {};
14275     this.keys = [];
14276     this.length = 0;
14277     this.addEvents({
14278         /**
14279          * @event clear
14280          * Fires when the collection is cleared.
14281          */
14282         "clear" : true,
14283         /**
14284          * @event add
14285          * Fires when an item is added to the collection.
14286          * @param {Number} index The index at which the item was added.
14287          * @param {Object} o The item added.
14288          * @param {String} key The key associated with the added item.
14289          */
14290         "add" : true,
14291         /**
14292          * @event replace
14293          * Fires when an item is replaced in the collection.
14294          * @param {String} key he key associated with the new added.
14295          * @param {Object} old The item being replaced.
14296          * @param {Object} new The new item.
14297          */
14298         "replace" : true,
14299         /**
14300          * @event remove
14301          * Fires when an item is removed from the collection.
14302          * @param {Object} o The item being removed.
14303          * @param {String} key (optional) The key associated with the removed item.
14304          */
14305         "remove" : true,
14306         "sort" : true
14307     });
14308     this.allowFunctions = allowFunctions === true;
14309     if(keyFn){
14310         this.getKey = keyFn;
14311     }
14312     Roo.util.MixedCollection.superclass.constructor.call(this);
14313 };
14314
14315 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14316     allowFunctions : false,
14317     
14318 /**
14319  * Adds an item to the collection.
14320  * @param {String} key The key to associate with the item
14321  * @param {Object} o The item to add.
14322  * @return {Object} The item added.
14323  */
14324     add : function(key, o){
14325         if(arguments.length == 1){
14326             o = arguments[0];
14327             key = this.getKey(o);
14328         }
14329         if(typeof key == "undefined" || key === null){
14330             this.length++;
14331             this.items.push(o);
14332             this.keys.push(null);
14333         }else{
14334             var old = this.map[key];
14335             if(old){
14336                 return this.replace(key, o);
14337             }
14338             this.length++;
14339             this.items.push(o);
14340             this.map[key] = o;
14341             this.keys.push(key);
14342         }
14343         this.fireEvent("add", this.length-1, o, key);
14344         return o;
14345     },
14346        
14347 /**
14348   * MixedCollection has a generic way to fetch keys if you implement getKey.
14349 <pre><code>
14350 // normal way
14351 var mc = new Roo.util.MixedCollection();
14352 mc.add(someEl.dom.id, someEl);
14353 mc.add(otherEl.dom.id, otherEl);
14354 //and so on
14355
14356 // using getKey
14357 var mc = new Roo.util.MixedCollection();
14358 mc.getKey = function(el){
14359    return el.dom.id;
14360 };
14361 mc.add(someEl);
14362 mc.add(otherEl);
14363
14364 // or via the constructor
14365 var mc = new Roo.util.MixedCollection(false, function(el){
14366    return el.dom.id;
14367 });
14368 mc.add(someEl);
14369 mc.add(otherEl);
14370 </code></pre>
14371  * @param o {Object} The item for which to find the key.
14372  * @return {Object} The key for the passed item.
14373  */
14374     getKey : function(o){
14375          return o.id; 
14376     },
14377    
14378 /**
14379  * Replaces an item in the collection.
14380  * @param {String} key The key associated with the item to replace, or the item to replace.
14381  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14382  * @return {Object}  The new item.
14383  */
14384     replace : function(key, o){
14385         if(arguments.length == 1){
14386             o = arguments[0];
14387             key = this.getKey(o);
14388         }
14389         var old = this.item(key);
14390         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14391              return this.add(key, o);
14392         }
14393         var index = this.indexOfKey(key);
14394         this.items[index] = o;
14395         this.map[key] = o;
14396         this.fireEvent("replace", key, old, o);
14397         return o;
14398     },
14399    
14400 /**
14401  * Adds all elements of an Array or an Object to the collection.
14402  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14403  * an Array of values, each of which are added to the collection.
14404  */
14405     addAll : function(objs){
14406         if(arguments.length > 1 || objs instanceof Array){
14407             var args = arguments.length > 1 ? arguments : objs;
14408             for(var i = 0, len = args.length; i < len; i++){
14409                 this.add(args[i]);
14410             }
14411         }else{
14412             for(var key in objs){
14413                 if(this.allowFunctions || typeof objs[key] != "function"){
14414                     this.add(key, objs[key]);
14415                 }
14416             }
14417         }
14418     },
14419    
14420 /**
14421  * Executes the specified function once for every item in the collection, passing each
14422  * item as the first and only parameter. returning false from the function will stop the iteration.
14423  * @param {Function} fn The function to execute for each item.
14424  * @param {Object} scope (optional) The scope in which to execute the function.
14425  */
14426     each : function(fn, scope){
14427         var items = [].concat(this.items); // each safe for removal
14428         for(var i = 0, len = items.length; i < len; i++){
14429             if(fn.call(scope || items[i], items[i], i, len) === false){
14430                 break;
14431             }
14432         }
14433     },
14434    
14435 /**
14436  * Executes the specified function once for every key in the collection, passing each
14437  * key, and its associated item as the first two parameters.
14438  * @param {Function} fn The function to execute for each item.
14439  * @param {Object} scope (optional) The scope in which to execute the function.
14440  */
14441     eachKey : function(fn, scope){
14442         for(var i = 0, len = this.keys.length; i < len; i++){
14443             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14444         }
14445     },
14446    
14447 /**
14448  * Returns the first item in the collection which elicits a true return value from the
14449  * passed selection function.
14450  * @param {Function} fn The selection function to execute for each item.
14451  * @param {Object} scope (optional) The scope in which to execute the function.
14452  * @return {Object} The first item in the collection which returned true from the selection function.
14453  */
14454     find : function(fn, scope){
14455         for(var i = 0, len = this.items.length; i < len; i++){
14456             if(fn.call(scope || window, this.items[i], this.keys[i])){
14457                 return this.items[i];
14458             }
14459         }
14460         return null;
14461     },
14462    
14463 /**
14464  * Inserts an item at the specified index in the collection.
14465  * @param {Number} index The index to insert the item at.
14466  * @param {String} key The key to associate with the new item, or the item itself.
14467  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14468  * @return {Object} The item inserted.
14469  */
14470     insert : function(index, key, o){
14471         if(arguments.length == 2){
14472             o = arguments[1];
14473             key = this.getKey(o);
14474         }
14475         if(index >= this.length){
14476             return this.add(key, o);
14477         }
14478         this.length++;
14479         this.items.splice(index, 0, o);
14480         if(typeof key != "undefined" && key != null){
14481             this.map[key] = o;
14482         }
14483         this.keys.splice(index, 0, key);
14484         this.fireEvent("add", index, o, key);
14485         return o;
14486     },
14487    
14488 /**
14489  * Removed an item from the collection.
14490  * @param {Object} o The item to remove.
14491  * @return {Object} The item removed.
14492  */
14493     remove : function(o){
14494         return this.removeAt(this.indexOf(o));
14495     },
14496    
14497 /**
14498  * Remove an item from a specified index in the collection.
14499  * @param {Number} index The index within the collection of the item to remove.
14500  */
14501     removeAt : function(index){
14502         if(index < this.length && index >= 0){
14503             this.length--;
14504             var o = this.items[index];
14505             this.items.splice(index, 1);
14506             var key = this.keys[index];
14507             if(typeof key != "undefined"){
14508                 delete this.map[key];
14509             }
14510             this.keys.splice(index, 1);
14511             this.fireEvent("remove", o, key);
14512         }
14513     },
14514    
14515 /**
14516  * Removed an item associated with the passed key fom the collection.
14517  * @param {String} key The key of the item to remove.
14518  */
14519     removeKey : function(key){
14520         return this.removeAt(this.indexOfKey(key));
14521     },
14522    
14523 /**
14524  * Returns the number of items in the collection.
14525  * @return {Number} the number of items in the collection.
14526  */
14527     getCount : function(){
14528         return this.length; 
14529     },
14530    
14531 /**
14532  * Returns index within the collection of the passed Object.
14533  * @param {Object} o The item to find the index of.
14534  * @return {Number} index of the item.
14535  */
14536     indexOf : function(o){
14537         if(!this.items.indexOf){
14538             for(var i = 0, len = this.items.length; i < len; i++){
14539                 if(this.items[i] == o) {
14540                     return i;
14541                 }
14542             }
14543             return -1;
14544         }else{
14545             return this.items.indexOf(o);
14546         }
14547     },
14548    
14549 /**
14550  * Returns index within the collection of the passed key.
14551  * @param {String} key The key to find the index of.
14552  * @return {Number} index of the key.
14553  */
14554     indexOfKey : function(key){
14555         if(!this.keys.indexOf){
14556             for(var i = 0, len = this.keys.length; i < len; i++){
14557                 if(this.keys[i] == key) {
14558                     return i;
14559                 }
14560             }
14561             return -1;
14562         }else{
14563             return this.keys.indexOf(key);
14564         }
14565     },
14566    
14567 /**
14568  * Returns the item associated with the passed key OR index. Key has priority over index.
14569  * @param {String/Number} key The key or index of the item.
14570  * @return {Object} The item associated with the passed key.
14571  */
14572     item : function(key){
14573         if (key === 'length') {
14574             return null;
14575         }
14576         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14577         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14578     },
14579     
14580 /**
14581  * Returns the item at the specified index.
14582  * @param {Number} index The index of the item.
14583  * @return {Object}
14584  */
14585     itemAt : function(index){
14586         return this.items[index];
14587     },
14588     
14589 /**
14590  * Returns the item associated with the passed key.
14591  * @param {String/Number} key The key of the item.
14592  * @return {Object} The item associated with the passed key.
14593  */
14594     key : function(key){
14595         return this.map[key];
14596     },
14597    
14598 /**
14599  * Returns true if the collection contains the passed Object as an item.
14600  * @param {Object} o  The Object to look for in the collection.
14601  * @return {Boolean} True if the collection contains the Object as an item.
14602  */
14603     contains : function(o){
14604         return this.indexOf(o) != -1;
14605     },
14606    
14607 /**
14608  * Returns true if the collection contains the passed Object as a key.
14609  * @param {String} key The key to look for in the collection.
14610  * @return {Boolean} True if the collection contains the Object as a key.
14611  */
14612     containsKey : function(key){
14613         return typeof this.map[key] != "undefined";
14614     },
14615    
14616 /**
14617  * Removes all items from the collection.
14618  */
14619     clear : function(){
14620         this.length = 0;
14621         this.items = [];
14622         this.keys = [];
14623         this.map = {};
14624         this.fireEvent("clear");
14625     },
14626    
14627 /**
14628  * Returns the first item in the collection.
14629  * @return {Object} the first item in the collection..
14630  */
14631     first : function(){
14632         return this.items[0]; 
14633     },
14634    
14635 /**
14636  * Returns the last item in the collection.
14637  * @return {Object} the last item in the collection..
14638  */
14639     last : function(){
14640         return this.items[this.length-1];   
14641     },
14642     
14643     _sort : function(property, dir, fn){
14644         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14645         fn = fn || function(a, b){
14646             return a-b;
14647         };
14648         var c = [], k = this.keys, items = this.items;
14649         for(var i = 0, len = items.length; i < len; i++){
14650             c[c.length] = {key: k[i], value: items[i], index: i};
14651         }
14652         c.sort(function(a, b){
14653             var v = fn(a[property], b[property]) * dsc;
14654             if(v == 0){
14655                 v = (a.index < b.index ? -1 : 1);
14656             }
14657             return v;
14658         });
14659         for(var i = 0, len = c.length; i < len; i++){
14660             items[i] = c[i].value;
14661             k[i] = c[i].key;
14662         }
14663         this.fireEvent("sort", this);
14664     },
14665     
14666     /**
14667      * Sorts this collection with the passed comparison function
14668      * @param {String} direction (optional) "ASC" or "DESC"
14669      * @param {Function} fn (optional) comparison function
14670      */
14671     sort : function(dir, fn){
14672         this._sort("value", dir, fn);
14673     },
14674     
14675     /**
14676      * Sorts this collection by keys
14677      * @param {String} direction (optional) "ASC" or "DESC"
14678      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14679      */
14680     keySort : function(dir, fn){
14681         this._sort("key", dir, fn || function(a, b){
14682             return String(a).toUpperCase()-String(b).toUpperCase();
14683         });
14684     },
14685     
14686     /**
14687      * Returns a range of items in this collection
14688      * @param {Number} startIndex (optional) defaults to 0
14689      * @param {Number} endIndex (optional) default to the last item
14690      * @return {Array} An array of items
14691      */
14692     getRange : function(start, end){
14693         var items = this.items;
14694         if(items.length < 1){
14695             return [];
14696         }
14697         start = start || 0;
14698         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14699         var r = [];
14700         if(start <= end){
14701             for(var i = start; i <= end; i++) {
14702                     r[r.length] = items[i];
14703             }
14704         }else{
14705             for(var i = start; i >= end; i--) {
14706                     r[r.length] = items[i];
14707             }
14708         }
14709         return r;
14710     },
14711         
14712     /**
14713      * Filter the <i>objects</i> in this collection by a specific property. 
14714      * Returns a new collection that has been filtered.
14715      * @param {String} property A property on your objects
14716      * @param {String/RegExp} value Either string that the property values 
14717      * should start with or a RegExp to test against the property
14718      * @return {MixedCollection} The new filtered collection
14719      */
14720     filter : function(property, value){
14721         if(!value.exec){ // not a regex
14722             value = String(value);
14723             if(value.length == 0){
14724                 return this.clone();
14725             }
14726             value = new RegExp("^" + Roo.escapeRe(value), "i");
14727         }
14728         return this.filterBy(function(o){
14729             return o && value.test(o[property]);
14730         });
14731         },
14732     
14733     /**
14734      * Filter by a function. * Returns a new collection that has been filtered.
14735      * The passed function will be called with each 
14736      * object in the collection. If the function returns true, the value is included 
14737      * otherwise it is filtered.
14738      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14739      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14740      * @return {MixedCollection} The new filtered collection
14741      */
14742     filterBy : function(fn, scope){
14743         var r = new Roo.util.MixedCollection();
14744         r.getKey = this.getKey;
14745         var k = this.keys, it = this.items;
14746         for(var i = 0, len = it.length; i < len; i++){
14747             if(fn.call(scope||this, it[i], k[i])){
14748                                 r.add(k[i], it[i]);
14749                         }
14750         }
14751         return r;
14752     },
14753     
14754     /**
14755      * Creates a duplicate of this collection
14756      * @return {MixedCollection}
14757      */
14758     clone : function(){
14759         var r = new Roo.util.MixedCollection();
14760         var k = this.keys, it = this.items;
14761         for(var i = 0, len = it.length; i < len; i++){
14762             r.add(k[i], it[i]);
14763         }
14764         r.getKey = this.getKey;
14765         return r;
14766     }
14767 });
14768 /**
14769  * Returns the item associated with the passed key or index.
14770  * @method
14771  * @param {String/Number} key The key or index of the item.
14772  * @return {Object} The item associated with the passed key.
14773  */
14774 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14775  * Based on:
14776  * Ext JS Library 1.1.1
14777  * Copyright(c) 2006-2007, Ext JS, LLC.
14778  *
14779  * Originally Released Under LGPL - original licence link has changed is not relivant.
14780  *
14781  * Fork - LGPL
14782  * <script type="text/javascript">
14783  */
14784 /**
14785  * @class Roo.util.JSON
14786  * Modified version of Douglas Crockford"s json.js that doesn"t
14787  * mess with the Object prototype 
14788  * http://www.json.org/js.html
14789  * @static
14790  */
14791 Roo.util.JSON = new (function(){
14792     var useHasOwn = {}.hasOwnProperty ? true : false;
14793     
14794     // crashes Safari in some instances
14795     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14796     
14797     var pad = function(n) {
14798         return n < 10 ? "0" + n : n;
14799     };
14800     
14801     var m = {
14802         "\b": '\\b',
14803         "\t": '\\t',
14804         "\n": '\\n',
14805         "\f": '\\f',
14806         "\r": '\\r',
14807         '"' : '\\"',
14808         "\\": '\\\\'
14809     };
14810
14811     var encodeString = function(s){
14812         if (/["\\\x00-\x1f]/.test(s)) {
14813             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14814                 var c = m[b];
14815                 if(c){
14816                     return c;
14817                 }
14818                 c = b.charCodeAt();
14819                 return "\\u00" +
14820                     Math.floor(c / 16).toString(16) +
14821                     (c % 16).toString(16);
14822             }) + '"';
14823         }
14824         return '"' + s + '"';
14825     };
14826     
14827     var encodeArray = function(o){
14828         var a = ["["], b, i, l = o.length, v;
14829             for (i = 0; i < l; i += 1) {
14830                 v = o[i];
14831                 switch (typeof v) {
14832                     case "undefined":
14833                     case "function":
14834                     case "unknown":
14835                         break;
14836                     default:
14837                         if (b) {
14838                             a.push(',');
14839                         }
14840                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14841                         b = true;
14842                 }
14843             }
14844             a.push("]");
14845             return a.join("");
14846     };
14847     
14848     var encodeDate = function(o){
14849         return '"' + o.getFullYear() + "-" +
14850                 pad(o.getMonth() + 1) + "-" +
14851                 pad(o.getDate()) + "T" +
14852                 pad(o.getHours()) + ":" +
14853                 pad(o.getMinutes()) + ":" +
14854                 pad(o.getSeconds()) + '"';
14855     };
14856     
14857     /**
14858      * Encodes an Object, Array or other value
14859      * @param {Mixed} o The variable to encode
14860      * @return {String} The JSON string
14861      */
14862     this.encode = function(o)
14863     {
14864         // should this be extended to fully wrap stringify..
14865         
14866         if(typeof o == "undefined" || o === null){
14867             return "null";
14868         }else if(o instanceof Array){
14869             return encodeArray(o);
14870         }else if(o instanceof Date){
14871             return encodeDate(o);
14872         }else if(typeof o == "string"){
14873             return encodeString(o);
14874         }else if(typeof o == "number"){
14875             return isFinite(o) ? String(o) : "null";
14876         }else if(typeof o == "boolean"){
14877             return String(o);
14878         }else {
14879             var a = ["{"], b, i, v;
14880             for (i in o) {
14881                 if(!useHasOwn || o.hasOwnProperty(i)) {
14882                     v = o[i];
14883                     switch (typeof v) {
14884                     case "undefined":
14885                     case "function":
14886                     case "unknown":
14887                         break;
14888                     default:
14889                         if(b){
14890                             a.push(',');
14891                         }
14892                         a.push(this.encode(i), ":",
14893                                 v === null ? "null" : this.encode(v));
14894                         b = true;
14895                     }
14896                 }
14897             }
14898             a.push("}");
14899             return a.join("");
14900         }
14901     };
14902     
14903     /**
14904      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14905      * @param {String} json The JSON string
14906      * @return {Object} The resulting object
14907      */
14908     this.decode = function(json){
14909         
14910         return  /** eval:var:json */ eval("(" + json + ')');
14911     };
14912 })();
14913 /** 
14914  * Shorthand for {@link Roo.util.JSON#encode}
14915  * @member Roo encode 
14916  * @method */
14917 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14918 /** 
14919  * Shorthand for {@link Roo.util.JSON#decode}
14920  * @member Roo decode 
14921  * @method */
14922 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14923 /*
14924  * Based on:
14925  * Ext JS Library 1.1.1
14926  * Copyright(c) 2006-2007, Ext JS, LLC.
14927  *
14928  * Originally Released Under LGPL - original licence link has changed is not relivant.
14929  *
14930  * Fork - LGPL
14931  * <script type="text/javascript">
14932  */
14933  
14934 /**
14935  * @class Roo.util.Format
14936  * Reusable data formatting functions
14937  * @static
14938  */
14939 Roo.util.Format = function(){
14940     var trimRe = /^\s+|\s+$/g;
14941     return {
14942         /**
14943          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14944          * @param {String} value The string to truncate
14945          * @param {Number} length The maximum length to allow before truncating
14946          * @return {String} The converted text
14947          */
14948         ellipsis : function(value, len){
14949             if(value && value.length > len){
14950                 return value.substr(0, len-3)+"...";
14951             }
14952             return value;
14953         },
14954
14955         /**
14956          * Checks a reference and converts it to empty string if it is undefined
14957          * @param {Mixed} value Reference to check
14958          * @return {Mixed} Empty string if converted, otherwise the original value
14959          */
14960         undef : function(value){
14961             return typeof value != "undefined" ? value : "";
14962         },
14963
14964         /**
14965          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14966          * @param {String} value The string to encode
14967          * @return {String} The encoded text
14968          */
14969         htmlEncode : function(value){
14970             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14971         },
14972
14973         /**
14974          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14975          * @param {String} value The string to decode
14976          * @return {String} The decoded text
14977          */
14978         htmlDecode : function(value){
14979             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14980         },
14981
14982         /**
14983          * Trims any whitespace from either side of a string
14984          * @param {String} value The text to trim
14985          * @return {String} The trimmed text
14986          */
14987         trim : function(value){
14988             return String(value).replace(trimRe, "");
14989         },
14990
14991         /**
14992          * Returns a substring from within an original string
14993          * @param {String} value The original text
14994          * @param {Number} start The start index of the substring
14995          * @param {Number} length The length of the substring
14996          * @return {String} The substring
14997          */
14998         substr : function(value, start, length){
14999             return String(value).substr(start, length);
15000         },
15001
15002         /**
15003          * Converts a string to all lower case letters
15004          * @param {String} value The text to convert
15005          * @return {String} The converted text
15006          */
15007         lowercase : function(value){
15008             return String(value).toLowerCase();
15009         },
15010
15011         /**
15012          * Converts a string to all upper case letters
15013          * @param {String} value The text to convert
15014          * @return {String} The converted text
15015          */
15016         uppercase : function(value){
15017             return String(value).toUpperCase();
15018         },
15019
15020         /**
15021          * Converts the first character only of a string to upper case
15022          * @param {String} value The text to convert
15023          * @return {String} The converted text
15024          */
15025         capitalize : function(value){
15026             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15027         },
15028
15029         // private
15030         call : function(value, fn){
15031             if(arguments.length > 2){
15032                 var args = Array.prototype.slice.call(arguments, 2);
15033                 args.unshift(value);
15034                  
15035                 return /** eval:var:value */  eval(fn).apply(window, args);
15036             }else{
15037                 /** eval:var:value */
15038                 return /** eval:var:value */ eval(fn).call(window, value);
15039             }
15040         },
15041
15042        
15043         /**
15044          * safer version of Math.toFixed..??/
15045          * @param {Number/String} value The numeric value to format
15046          * @param {Number/String} value Decimal places 
15047          * @return {String} The formatted currency string
15048          */
15049         toFixed : function(v, n)
15050         {
15051             // why not use to fixed - precision is buggered???
15052             if (!n) {
15053                 return Math.round(v-0);
15054             }
15055             var fact = Math.pow(10,n+1);
15056             v = (Math.round((v-0)*fact))/fact;
15057             var z = (''+fact).substring(2);
15058             if (v == Math.floor(v)) {
15059                 return Math.floor(v) + '.' + z;
15060             }
15061             
15062             // now just padd decimals..
15063             var ps = String(v).split('.');
15064             var fd = (ps[1] + z);
15065             var r = fd.substring(0,n); 
15066             var rm = fd.substring(n); 
15067             if (rm < 5) {
15068                 return ps[0] + '.' + r;
15069             }
15070             r*=1; // turn it into a number;
15071             r++;
15072             if (String(r).length != n) {
15073                 ps[0]*=1;
15074                 ps[0]++;
15075                 r = String(r).substring(1); // chop the end off.
15076             }
15077             
15078             return ps[0] + '.' + r;
15079              
15080         },
15081         
15082         /**
15083          * Format a number as US currency
15084          * @param {Number/String} value The numeric value to format
15085          * @return {String} The formatted currency string
15086          */
15087         usMoney : function(v){
15088             return '$' + Roo.util.Format.number(v);
15089         },
15090         
15091         /**
15092          * Format a number
15093          * eventually this should probably emulate php's number_format
15094          * @param {Number/String} value The numeric value to format
15095          * @param {Number} decimals number of decimal places
15096          * @param {String} delimiter for thousands (default comma)
15097          * @return {String} The formatted currency string
15098          */
15099         number : function(v, decimals, thousandsDelimiter)
15100         {
15101             // multiply and round.
15102             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15103             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15104             
15105             var mul = Math.pow(10, decimals);
15106             var zero = String(mul).substring(1);
15107             v = (Math.round((v-0)*mul))/mul;
15108             
15109             // if it's '0' number.. then
15110             
15111             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15112             v = String(v);
15113             var ps = v.split('.');
15114             var whole = ps[0];
15115             
15116             var r = /(\d+)(\d{3})/;
15117             // add comma's
15118             
15119             if(thousandsDelimiter.length != 0) {
15120                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15121             } 
15122             
15123             var sub = ps[1] ?
15124                     // has decimals..
15125                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15126                     // does not have decimals
15127                     (decimals ? ('.' + zero) : '');
15128             
15129             
15130             return whole + sub ;
15131         },
15132         
15133         /**
15134          * Parse a value into a formatted date using the specified format pattern.
15135          * @param {Mixed} value The value to format
15136          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15137          * @return {String} The formatted date string
15138          */
15139         date : function(v, format){
15140             if(!v){
15141                 return "";
15142             }
15143             if(!(v instanceof Date)){
15144                 v = new Date(Date.parse(v));
15145             }
15146             return v.dateFormat(format || Roo.util.Format.defaults.date);
15147         },
15148
15149         /**
15150          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15151          * @param {String} format Any valid date format string
15152          * @return {Function} The date formatting function
15153          */
15154         dateRenderer : function(format){
15155             return function(v){
15156                 return Roo.util.Format.date(v, format);  
15157             };
15158         },
15159
15160         // private
15161         stripTagsRE : /<\/?[^>]+>/gi,
15162         
15163         /**
15164          * Strips all HTML tags
15165          * @param {Mixed} value The text from which to strip tags
15166          * @return {String} The stripped text
15167          */
15168         stripTags : function(v){
15169             return !v ? v : String(v).replace(this.stripTagsRE, "");
15170         },
15171         
15172         /**
15173          * Size in Mb,Gb etc.
15174          * @param {Number} value The number to be formated
15175          * @param {number} decimals how many decimal places
15176          * @return {String} the formated string
15177          */
15178         size : function(value, decimals)
15179         {
15180             var sizes = ['b', 'k', 'M', 'G', 'T'];
15181             if (value == 0) {
15182                 return 0;
15183             }
15184             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15185             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15186         }
15187         
15188         
15189         
15190     };
15191 }();
15192 Roo.util.Format.defaults = {
15193     date : 'd/M/Y'
15194 };/*
15195  * Based on:
15196  * Ext JS Library 1.1.1
15197  * Copyright(c) 2006-2007, Ext JS, LLC.
15198  *
15199  * Originally Released Under LGPL - original licence link has changed is not relivant.
15200  *
15201  * Fork - LGPL
15202  * <script type="text/javascript">
15203  */
15204
15205
15206  
15207
15208 /**
15209  * @class Roo.MasterTemplate
15210  * @extends Roo.Template
15211  * Provides a template that can have child templates. The syntax is:
15212 <pre><code>
15213 var t = new Roo.MasterTemplate(
15214         '&lt;select name="{name}"&gt;',
15215                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15216         '&lt;/select&gt;'
15217 );
15218 t.add('options', {value: 'foo', text: 'bar'});
15219 // or you can add multiple child elements in one shot
15220 t.addAll('options', [
15221     {value: 'foo', text: 'bar'},
15222     {value: 'foo2', text: 'bar2'},
15223     {value: 'foo3', text: 'bar3'}
15224 ]);
15225 // then append, applying the master template values
15226 t.append('my-form', {name: 'my-select'});
15227 </code></pre>
15228 * A name attribute for the child template is not required if you have only one child
15229 * template or you want to refer to them by index.
15230  */
15231 Roo.MasterTemplate = function(){
15232     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15233     this.originalHtml = this.html;
15234     var st = {};
15235     var m, re = this.subTemplateRe;
15236     re.lastIndex = 0;
15237     var subIndex = 0;
15238     while(m = re.exec(this.html)){
15239         var name = m[1], content = m[2];
15240         st[subIndex] = {
15241             name: name,
15242             index: subIndex,
15243             buffer: [],
15244             tpl : new Roo.Template(content)
15245         };
15246         if(name){
15247             st[name] = st[subIndex];
15248         }
15249         st[subIndex].tpl.compile();
15250         st[subIndex].tpl.call = this.call.createDelegate(this);
15251         subIndex++;
15252     }
15253     this.subCount = subIndex;
15254     this.subs = st;
15255 };
15256 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15257     /**
15258     * The regular expression used to match sub templates
15259     * @type RegExp
15260     * @property
15261     */
15262     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15263
15264     /**
15265      * Applies the passed values to a child template.
15266      * @param {String/Number} name (optional) The name or index of the child template
15267      * @param {Array/Object} values The values to be applied to the template
15268      * @return {MasterTemplate} this
15269      */
15270      add : function(name, values){
15271         if(arguments.length == 1){
15272             values = arguments[0];
15273             name = 0;
15274         }
15275         var s = this.subs[name];
15276         s.buffer[s.buffer.length] = s.tpl.apply(values);
15277         return this;
15278     },
15279
15280     /**
15281      * Applies all the passed values to a child template.
15282      * @param {String/Number} name (optional) The name or index of the child template
15283      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15284      * @param {Boolean} reset (optional) True to reset the template first
15285      * @return {MasterTemplate} this
15286      */
15287     fill : function(name, values, reset){
15288         var a = arguments;
15289         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15290             values = a[0];
15291             name = 0;
15292             reset = a[1];
15293         }
15294         if(reset){
15295             this.reset();
15296         }
15297         for(var i = 0, len = values.length; i < len; i++){
15298             this.add(name, values[i]);
15299         }
15300         return this;
15301     },
15302
15303     /**
15304      * Resets the template for reuse
15305      * @return {MasterTemplate} this
15306      */
15307      reset : function(){
15308         var s = this.subs;
15309         for(var i = 0; i < this.subCount; i++){
15310             s[i].buffer = [];
15311         }
15312         return this;
15313     },
15314
15315     applyTemplate : function(values){
15316         var s = this.subs;
15317         var replaceIndex = -1;
15318         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15319             return s[++replaceIndex].buffer.join("");
15320         });
15321         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15322     },
15323
15324     apply : function(){
15325         return this.applyTemplate.apply(this, arguments);
15326     },
15327
15328     compile : function(){return this;}
15329 });
15330
15331 /**
15332  * Alias for fill().
15333  * @method
15334  */
15335 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15336  /**
15337  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15338  * var tpl = Roo.MasterTemplate.from('element-id');
15339  * @param {String/HTMLElement} el
15340  * @param {Object} config
15341  * @static
15342  */
15343 Roo.MasterTemplate.from = function(el, config){
15344     el = Roo.getDom(el);
15345     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15346 };/*
15347  * Based on:
15348  * Ext JS Library 1.1.1
15349  * Copyright(c) 2006-2007, Ext JS, LLC.
15350  *
15351  * Originally Released Under LGPL - original licence link has changed is not relivant.
15352  *
15353  * Fork - LGPL
15354  * <script type="text/javascript">
15355  */
15356
15357  
15358 /**
15359  * @class Roo.util.CSS
15360  * Utility class for manipulating CSS rules
15361  * @static
15362
15363  */
15364 Roo.util.CSS = function(){
15365         var rules = null;
15366         var doc = document;
15367
15368     var camelRe = /(-[a-z])/gi;
15369     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15370
15371    return {
15372    /**
15373     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15374     * tag and appended to the HEAD of the document.
15375     * @param {String|Object} cssText The text containing the css rules
15376     * @param {String} id An id to add to the stylesheet for later removal
15377     * @return {StyleSheet}
15378     */
15379     createStyleSheet : function(cssText, id){
15380         var ss;
15381         var head = doc.getElementsByTagName("head")[0];
15382         var nrules = doc.createElement("style");
15383         nrules.setAttribute("type", "text/css");
15384         if(id){
15385             nrules.setAttribute("id", id);
15386         }
15387         if (typeof(cssText) != 'string') {
15388             // support object maps..
15389             // not sure if this a good idea.. 
15390             // perhaps it should be merged with the general css handling
15391             // and handle js style props.
15392             var cssTextNew = [];
15393             for(var n in cssText) {
15394                 var citems = [];
15395                 for(var k in cssText[n]) {
15396                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15397                 }
15398                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15399                 
15400             }
15401             cssText = cssTextNew.join("\n");
15402             
15403         }
15404        
15405        
15406        if(Roo.isIE){
15407            head.appendChild(nrules);
15408            ss = nrules.styleSheet;
15409            ss.cssText = cssText;
15410        }else{
15411            try{
15412                 nrules.appendChild(doc.createTextNode(cssText));
15413            }catch(e){
15414                nrules.cssText = cssText; 
15415            }
15416            head.appendChild(nrules);
15417            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15418        }
15419        this.cacheStyleSheet(ss);
15420        return ss;
15421    },
15422
15423    /**
15424     * Removes a style or link tag by id
15425     * @param {String} id The id of the tag
15426     */
15427    removeStyleSheet : function(id){
15428        var existing = doc.getElementById(id);
15429        if(existing){
15430            existing.parentNode.removeChild(existing);
15431        }
15432    },
15433
15434    /**
15435     * Dynamically swaps an existing stylesheet reference for a new one
15436     * @param {String} id The id of an existing link tag to remove
15437     * @param {String} url The href of the new stylesheet to include
15438     */
15439    swapStyleSheet : function(id, url){
15440        this.removeStyleSheet(id);
15441        var ss = doc.createElement("link");
15442        ss.setAttribute("rel", "stylesheet");
15443        ss.setAttribute("type", "text/css");
15444        ss.setAttribute("id", id);
15445        ss.setAttribute("href", url);
15446        doc.getElementsByTagName("head")[0].appendChild(ss);
15447    },
15448    
15449    /**
15450     * Refresh the rule cache if you have dynamically added stylesheets
15451     * @return {Object} An object (hash) of rules indexed by selector
15452     */
15453    refreshCache : function(){
15454        return this.getRules(true);
15455    },
15456
15457    // private
15458    cacheStyleSheet : function(stylesheet){
15459        if(!rules){
15460            rules = {};
15461        }
15462        try{// try catch for cross domain access issue
15463            var ssRules = stylesheet.cssRules || stylesheet.rules;
15464            for(var j = ssRules.length-1; j >= 0; --j){
15465                rules[ssRules[j].selectorText] = ssRules[j];
15466            }
15467        }catch(e){}
15468    },
15469    
15470    /**
15471     * Gets all css rules for the document
15472     * @param {Boolean} refreshCache true to refresh the internal cache
15473     * @return {Object} An object (hash) of rules indexed by selector
15474     */
15475    getRules : function(refreshCache){
15476                 if(rules == null || refreshCache){
15477                         rules = {};
15478                         var ds = doc.styleSheets;
15479                         for(var i =0, len = ds.length; i < len; i++){
15480                             try{
15481                         this.cacheStyleSheet(ds[i]);
15482                     }catch(e){} 
15483                 }
15484                 }
15485                 return rules;
15486         },
15487         
15488         /**
15489     * Gets an an individual CSS rule by selector(s)
15490     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15491     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15492     * @return {CSSRule} The CSS rule or null if one is not found
15493     */
15494    getRule : function(selector, refreshCache){
15495                 var rs = this.getRules(refreshCache);
15496                 if(!(selector instanceof Array)){
15497                     return rs[selector];
15498                 }
15499                 for(var i = 0; i < selector.length; i++){
15500                         if(rs[selector[i]]){
15501                                 return rs[selector[i]];
15502                         }
15503                 }
15504                 return null;
15505         },
15506         
15507         
15508         /**
15509     * Updates a rule property
15510     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15511     * @param {String} property The css property
15512     * @param {String} value The new value for the property
15513     * @return {Boolean} true If a rule was found and updated
15514     */
15515    updateRule : function(selector, property, value){
15516                 if(!(selector instanceof Array)){
15517                         var rule = this.getRule(selector);
15518                         if(rule){
15519                                 rule.style[property.replace(camelRe, camelFn)] = value;
15520                                 return true;
15521                         }
15522                 }else{
15523                         for(var i = 0; i < selector.length; i++){
15524                                 if(this.updateRule(selector[i], property, value)){
15525                                         return true;
15526                                 }
15527                         }
15528                 }
15529                 return false;
15530         }
15531    };   
15532 }();/*
15533  * Based on:
15534  * Ext JS Library 1.1.1
15535  * Copyright(c) 2006-2007, Ext JS, LLC.
15536  *
15537  * Originally Released Under LGPL - original licence link has changed is not relivant.
15538  *
15539  * Fork - LGPL
15540  * <script type="text/javascript">
15541  */
15542
15543  
15544
15545 /**
15546  * @class Roo.util.ClickRepeater
15547  * @extends Roo.util.Observable
15548  * 
15549  * A wrapper class which can be applied to any element. Fires a "click" event while the
15550  * mouse is pressed. The interval between firings may be specified in the config but
15551  * defaults to 10 milliseconds.
15552  * 
15553  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15554  * 
15555  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15556  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15557  * Similar to an autorepeat key delay.
15558  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15559  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15560  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15561  *           "interval" and "delay" are ignored. "immediate" is honored.
15562  * @cfg {Boolean} preventDefault True to prevent the default click event
15563  * @cfg {Boolean} stopDefault True to stop the default click event
15564  * 
15565  * @history
15566  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15567  *     2007-02-02 jvs Renamed to ClickRepeater
15568  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15569  *
15570  *  @constructor
15571  * @param {String/HTMLElement/Element} el The element to listen on
15572  * @param {Object} config
15573  **/
15574 Roo.util.ClickRepeater = function(el, config)
15575 {
15576     this.el = Roo.get(el);
15577     this.el.unselectable();
15578
15579     Roo.apply(this, config);
15580
15581     this.addEvents({
15582     /**
15583      * @event mousedown
15584      * Fires when the mouse button is depressed.
15585      * @param {Roo.util.ClickRepeater} this
15586      */
15587         "mousedown" : true,
15588     /**
15589      * @event click
15590      * Fires on a specified interval during the time the element is pressed.
15591      * @param {Roo.util.ClickRepeater} this
15592      */
15593         "click" : true,
15594     /**
15595      * @event mouseup
15596      * Fires when the mouse key is released.
15597      * @param {Roo.util.ClickRepeater} this
15598      */
15599         "mouseup" : true
15600     });
15601
15602     this.el.on("mousedown", this.handleMouseDown, this);
15603     if(this.preventDefault || this.stopDefault){
15604         this.el.on("click", function(e){
15605             if(this.preventDefault){
15606                 e.preventDefault();
15607             }
15608             if(this.stopDefault){
15609                 e.stopEvent();
15610             }
15611         }, this);
15612     }
15613
15614     // allow inline handler
15615     if(this.handler){
15616         this.on("click", this.handler,  this.scope || this);
15617     }
15618
15619     Roo.util.ClickRepeater.superclass.constructor.call(this);
15620 };
15621
15622 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15623     interval : 20,
15624     delay: 250,
15625     preventDefault : true,
15626     stopDefault : false,
15627     timer : 0,
15628
15629     // private
15630     handleMouseDown : function(){
15631         clearTimeout(this.timer);
15632         this.el.blur();
15633         if(this.pressClass){
15634             this.el.addClass(this.pressClass);
15635         }
15636         this.mousedownTime = new Date();
15637
15638         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15639         this.el.on("mouseout", this.handleMouseOut, this);
15640
15641         this.fireEvent("mousedown", this);
15642         this.fireEvent("click", this);
15643         
15644         this.timer = this.click.defer(this.delay || this.interval, this);
15645     },
15646
15647     // private
15648     click : function(){
15649         this.fireEvent("click", this);
15650         this.timer = this.click.defer(this.getInterval(), this);
15651     },
15652
15653     // private
15654     getInterval: function(){
15655         if(!this.accelerate){
15656             return this.interval;
15657         }
15658         var pressTime = this.mousedownTime.getElapsed();
15659         if(pressTime < 500){
15660             return 400;
15661         }else if(pressTime < 1700){
15662             return 320;
15663         }else if(pressTime < 2600){
15664             return 250;
15665         }else if(pressTime < 3500){
15666             return 180;
15667         }else if(pressTime < 4400){
15668             return 140;
15669         }else if(pressTime < 5300){
15670             return 80;
15671         }else if(pressTime < 6200){
15672             return 50;
15673         }else{
15674             return 10;
15675         }
15676     },
15677
15678     // private
15679     handleMouseOut : function(){
15680         clearTimeout(this.timer);
15681         if(this.pressClass){
15682             this.el.removeClass(this.pressClass);
15683         }
15684         this.el.on("mouseover", this.handleMouseReturn, this);
15685     },
15686
15687     // private
15688     handleMouseReturn : function(){
15689         this.el.un("mouseover", this.handleMouseReturn);
15690         if(this.pressClass){
15691             this.el.addClass(this.pressClass);
15692         }
15693         this.click();
15694     },
15695
15696     // private
15697     handleMouseUp : function(){
15698         clearTimeout(this.timer);
15699         this.el.un("mouseover", this.handleMouseReturn);
15700         this.el.un("mouseout", this.handleMouseOut);
15701         Roo.get(document).un("mouseup", this.handleMouseUp);
15702         this.el.removeClass(this.pressClass);
15703         this.fireEvent("mouseup", this);
15704     }
15705 });/**
15706  * @class Roo.util.Clipboard
15707  * @static
15708  * 
15709  * Clipboard UTILS
15710  * 
15711  **/
15712 Roo.util.Clipboard = {
15713     /**
15714      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15715      * @param {String} text to copy to clipboard
15716      */
15717     write : function(text) {
15718         // navigator clipboard api needs a secure context (https)
15719         if (navigator.clipboard && window.isSecureContext) {
15720             // navigator clipboard api method'
15721             navigator.clipboard.writeText(text);
15722             return ;
15723         } 
15724         // text area method
15725         var ta = document.createElement("textarea");
15726         ta.value = text;
15727         // make the textarea out of viewport
15728         ta.style.position = "fixed";
15729         ta.style.left = "-999999px";
15730         ta.style.top = "-999999px";
15731         document.body.appendChild(ta);
15732         ta.focus();
15733         ta.select();
15734         document.execCommand('copy');
15735         (function() {
15736             ta.remove();
15737         }).defer(100);
15738         
15739     }
15740         
15741 }
15742     /*
15743  * Based on:
15744  * Ext JS Library 1.1.1
15745  * Copyright(c) 2006-2007, Ext JS, LLC.
15746  *
15747  * Originally Released Under LGPL - original licence link has changed is not relivant.
15748  *
15749  * Fork - LGPL
15750  * <script type="text/javascript">
15751  */
15752
15753  
15754 /**
15755  * @class Roo.KeyNav
15756  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15757  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15758  * way to implement custom navigation schemes for any UI component.</p>
15759  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15760  * pageUp, pageDown, del, home, end.  Usage:</p>
15761  <pre><code>
15762 var nav = new Roo.KeyNav("my-element", {
15763     "left" : function(e){
15764         this.moveLeft(e.ctrlKey);
15765     },
15766     "right" : function(e){
15767         this.moveRight(e.ctrlKey);
15768     },
15769     "enter" : function(e){
15770         this.save();
15771     },
15772     scope : this
15773 });
15774 </code></pre>
15775  * @constructor
15776  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15777  * @param {Object} config The config
15778  */
15779 Roo.KeyNav = function(el, config){
15780     this.el = Roo.get(el);
15781     Roo.apply(this, config);
15782     if(!this.disabled){
15783         this.disabled = true;
15784         this.enable();
15785     }
15786 };
15787
15788 Roo.KeyNav.prototype = {
15789     /**
15790      * @cfg {Boolean} disabled
15791      * True to disable this KeyNav instance (defaults to false)
15792      */
15793     disabled : false,
15794     /**
15795      * @cfg {String} defaultEventAction
15796      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15797      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15798      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15799      */
15800     defaultEventAction: "stopEvent",
15801     /**
15802      * @cfg {Boolean} forceKeyDown
15803      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15804      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15805      * handle keydown instead of keypress.
15806      */
15807     forceKeyDown : false,
15808
15809     // private
15810     prepareEvent : function(e){
15811         var k = e.getKey();
15812         var h = this.keyToHandler[k];
15813         //if(h && this[h]){
15814         //    e.stopPropagation();
15815         //}
15816         if(Roo.isSafari && h && k >= 37 && k <= 40){
15817             e.stopEvent();
15818         }
15819     },
15820
15821     // private
15822     relay : function(e){
15823         var k = e.getKey();
15824         var h = this.keyToHandler[k];
15825         if(h && this[h]){
15826             if(this.doRelay(e, this[h], h) !== true){
15827                 e[this.defaultEventAction]();
15828             }
15829         }
15830     },
15831
15832     // private
15833     doRelay : function(e, h, hname){
15834         return h.call(this.scope || this, e);
15835     },
15836
15837     // possible handlers
15838     enter : false,
15839     left : false,
15840     right : false,
15841     up : false,
15842     down : false,
15843     tab : false,
15844     esc : false,
15845     pageUp : false,
15846     pageDown : false,
15847     del : false,
15848     home : false,
15849     end : false,
15850
15851     // quick lookup hash
15852     keyToHandler : {
15853         37 : "left",
15854         39 : "right",
15855         38 : "up",
15856         40 : "down",
15857         33 : "pageUp",
15858         34 : "pageDown",
15859         46 : "del",
15860         36 : "home",
15861         35 : "end",
15862         13 : "enter",
15863         27 : "esc",
15864         9  : "tab"
15865     },
15866
15867         /**
15868          * Enable this KeyNav
15869          */
15870         enable: function(){
15871                 if(this.disabled){
15872             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15873             // the EventObject will normalize Safari automatically
15874             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15875                 this.el.on("keydown", this.relay,  this);
15876             }else{
15877                 this.el.on("keydown", this.prepareEvent,  this);
15878                 this.el.on("keypress", this.relay,  this);
15879             }
15880                     this.disabled = false;
15881                 }
15882         },
15883
15884         /**
15885          * Disable this KeyNav
15886          */
15887         disable: function(){
15888                 if(!this.disabled){
15889                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15890                 this.el.un("keydown", this.relay);
15891             }else{
15892                 this.el.un("keydown", this.prepareEvent);
15893                 this.el.un("keypress", this.relay);
15894             }
15895                     this.disabled = true;
15896                 }
15897         }
15898 };/*
15899  * Based on:
15900  * Ext JS Library 1.1.1
15901  * Copyright(c) 2006-2007, Ext JS, LLC.
15902  *
15903  * Originally Released Under LGPL - original licence link has changed is not relivant.
15904  *
15905  * Fork - LGPL
15906  * <script type="text/javascript">
15907  */
15908
15909  
15910 /**
15911  * @class Roo.KeyMap
15912  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15913  * The constructor accepts the same config object as defined by {@link #addBinding}.
15914  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15915  * combination it will call the function with this signature (if the match is a multi-key
15916  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15917  * A KeyMap can also handle a string representation of keys.<br />
15918  * Usage:
15919  <pre><code>
15920 // map one key by key code
15921 var map = new Roo.KeyMap("my-element", {
15922     key: 13, // or Roo.EventObject.ENTER
15923     fn: myHandler,
15924     scope: myObject
15925 });
15926
15927 // map multiple keys to one action by string
15928 var map = new Roo.KeyMap("my-element", {
15929     key: "a\r\n\t",
15930     fn: myHandler,
15931     scope: myObject
15932 });
15933
15934 // map multiple keys to multiple actions by strings and array of codes
15935 var map = new Roo.KeyMap("my-element", [
15936     {
15937         key: [10,13],
15938         fn: function(){ alert("Return was pressed"); }
15939     }, {
15940         key: "abc",
15941         fn: function(){ alert('a, b or c was pressed'); }
15942     }, {
15943         key: "\t",
15944         ctrl:true,
15945         shift:true,
15946         fn: function(){ alert('Control + shift + tab was pressed.'); }
15947     }
15948 ]);
15949 </code></pre>
15950  * <b>Note: A KeyMap starts enabled</b>
15951  * @constructor
15952  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15953  * @param {Object} config The config (see {@link #addBinding})
15954  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15955  */
15956 Roo.KeyMap = function(el, config, eventName){
15957     this.el  = Roo.get(el);
15958     this.eventName = eventName || "keydown";
15959     this.bindings = [];
15960     if(config){
15961         this.addBinding(config);
15962     }
15963     this.enable();
15964 };
15965
15966 Roo.KeyMap.prototype = {
15967     /**
15968      * True to stop the event from bubbling and prevent the default browser action if the
15969      * key was handled by the KeyMap (defaults to false)
15970      * @type Boolean
15971      */
15972     stopEvent : false,
15973
15974     /**
15975      * Add a new binding to this KeyMap. The following config object properties are supported:
15976      * <pre>
15977 Property    Type             Description
15978 ----------  ---------------  ----------------------------------------------------------------------
15979 key         String/Array     A single keycode or an array of keycodes to handle
15980 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15981 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15982 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15983 fn          Function         The function to call when KeyMap finds the expected key combination
15984 scope       Object           The scope of the callback function
15985 </pre>
15986      *
15987      * Usage:
15988      * <pre><code>
15989 // Create a KeyMap
15990 var map = new Roo.KeyMap(document, {
15991     key: Roo.EventObject.ENTER,
15992     fn: handleKey,
15993     scope: this
15994 });
15995
15996 //Add a new binding to the existing KeyMap later
15997 map.addBinding({
15998     key: 'abc',
15999     shift: true,
16000     fn: handleKey,
16001     scope: this
16002 });
16003 </code></pre>
16004      * @param {Object/Array} config A single KeyMap config or an array of configs
16005      */
16006         addBinding : function(config){
16007         if(config instanceof Array){
16008             for(var i = 0, len = config.length; i < len; i++){
16009                 this.addBinding(config[i]);
16010             }
16011             return;
16012         }
16013         var keyCode = config.key,
16014             shift = config.shift, 
16015             ctrl = config.ctrl, 
16016             alt = config.alt,
16017             fn = config.fn,
16018             scope = config.scope;
16019         if(typeof keyCode == "string"){
16020             var ks = [];
16021             var keyString = keyCode.toUpperCase();
16022             for(var j = 0, len = keyString.length; j < len; j++){
16023                 ks.push(keyString.charCodeAt(j));
16024             }
16025             keyCode = ks;
16026         }
16027         var keyArray = keyCode instanceof Array;
16028         var handler = function(e){
16029             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16030                 var k = e.getKey();
16031                 if(keyArray){
16032                     for(var i = 0, len = keyCode.length; i < len; i++){
16033                         if(keyCode[i] == k){
16034                           if(this.stopEvent){
16035                               e.stopEvent();
16036                           }
16037                           fn.call(scope || window, k, e);
16038                           return;
16039                         }
16040                     }
16041                 }else{
16042                     if(k == keyCode){
16043                         if(this.stopEvent){
16044                            e.stopEvent();
16045                         }
16046                         fn.call(scope || window, k, e);
16047                     }
16048                 }
16049             }
16050         };
16051         this.bindings.push(handler);  
16052         },
16053
16054     /**
16055      * Shorthand for adding a single key listener
16056      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16057      * following options:
16058      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16059      * @param {Function} fn The function to call
16060      * @param {Object} scope (optional) The scope of the function
16061      */
16062     on : function(key, fn, scope){
16063         var keyCode, shift, ctrl, alt;
16064         if(typeof key == "object" && !(key instanceof Array)){
16065             keyCode = key.key;
16066             shift = key.shift;
16067             ctrl = key.ctrl;
16068             alt = key.alt;
16069         }else{
16070             keyCode = key;
16071         }
16072         this.addBinding({
16073             key: keyCode,
16074             shift: shift,
16075             ctrl: ctrl,
16076             alt: alt,
16077             fn: fn,
16078             scope: scope
16079         })
16080     },
16081
16082     // private
16083     handleKeyDown : function(e){
16084             if(this.enabled){ //just in case
16085             var b = this.bindings;
16086             for(var i = 0, len = b.length; i < len; i++){
16087                 b[i].call(this, e);
16088             }
16089             }
16090         },
16091         
16092         /**
16093          * Returns true if this KeyMap is enabled
16094          * @return {Boolean} 
16095          */
16096         isEnabled : function(){
16097             return this.enabled;  
16098         },
16099         
16100         /**
16101          * Enables this KeyMap
16102          */
16103         enable: function(){
16104                 if(!this.enabled){
16105                     this.el.on(this.eventName, this.handleKeyDown, this);
16106                     this.enabled = true;
16107                 }
16108         },
16109
16110         /**
16111          * Disable this KeyMap
16112          */
16113         disable: function(){
16114                 if(this.enabled){
16115                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16116                     this.enabled = false;
16117                 }
16118         }
16119 };/*
16120  * Based on:
16121  * Ext JS Library 1.1.1
16122  * Copyright(c) 2006-2007, Ext JS, LLC.
16123  *
16124  * Originally Released Under LGPL - original licence link has changed is not relivant.
16125  *
16126  * Fork - LGPL
16127  * <script type="text/javascript">
16128  */
16129
16130  
16131 /**
16132  * @class Roo.util.TextMetrics
16133  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16134  * wide, in pixels, a given block of text will be.
16135  * @static
16136  */
16137 Roo.util.TextMetrics = function(){
16138     var shared;
16139     return {
16140         /**
16141          * Measures the size of the specified text
16142          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16143          * that can affect the size of the rendered text
16144          * @param {String} text The text to measure
16145          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16146          * in order to accurately measure the text height
16147          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16148          */
16149         measure : function(el, text, fixedWidth){
16150             if(!shared){
16151                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16152             }
16153             shared.bind(el);
16154             shared.setFixedWidth(fixedWidth || 'auto');
16155             return shared.getSize(text);
16156         },
16157
16158         /**
16159          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16160          * the overhead of multiple calls to initialize the style properties on each measurement.
16161          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16162          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16163          * in order to accurately measure the text height
16164          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16165          */
16166         createInstance : function(el, fixedWidth){
16167             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16168         }
16169     };
16170 }();
16171
16172 /**
16173  * @class Roo.util.TextMetrics.Instance
16174  * Instance of  TextMetrics Calcuation
16175  * @constructor
16176  * Create a new TextMetrics Instance
16177  * @param {Object} bindto
16178  * @param {Boolean} fixedWidth
16179  */
16180
16181 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16182 {
16183     var ml = new Roo.Element(document.createElement('div'));
16184     document.body.appendChild(ml.dom);
16185     ml.position('absolute');
16186     ml.setLeftTop(-1000, -1000);
16187     ml.hide();
16188
16189     if(fixedWidth){
16190         ml.setWidth(fixedWidth);
16191     }
16192      
16193     var instance = {
16194         /**
16195          * Returns the size of the specified text based on the internal element's style and width properties
16196          * @param {String} text The text to measure
16197          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16198          */
16199         getSize : function(text){
16200             ml.update(text);
16201             var s = ml.getSize();
16202             ml.update('');
16203             return s;
16204         },
16205
16206         /**
16207          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16208          * that can affect the size of the rendered text
16209          * @param {String/HTMLElement} el The element, dom node or id
16210          */
16211         bind : function(el){
16212             ml.setStyle(
16213                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16214             );
16215         },
16216
16217         /**
16218          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16219          * to set a fixed width in order to accurately measure the text height.
16220          * @param {Number} width The width to set on the element
16221          */
16222         setFixedWidth : function(width){
16223             ml.setWidth(width);
16224         },
16225
16226         /**
16227          * Returns the measured width of the specified text
16228          * @param {String} text The text to measure
16229          * @return {Number} width The width in pixels
16230          */
16231         getWidth : function(text){
16232             ml.dom.style.width = 'auto';
16233             return this.getSize(text).width;
16234         },
16235
16236         /**
16237          * Returns the measured height of the specified text.  For multiline text, be sure to call
16238          * {@link #setFixedWidth} if necessary.
16239          * @param {String} text The text to measure
16240          * @return {Number} height The height in pixels
16241          */
16242         getHeight : function(text){
16243             return this.getSize(text).height;
16244         }
16245     };
16246
16247     instance.bind(bindTo);
16248
16249     return instance;
16250 };
16251
16252 // backwards compat
16253 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16254  * Based on:
16255  * Ext JS Library 1.1.1
16256  * Copyright(c) 2006-2007, Ext JS, LLC.
16257  *
16258  * Originally Released Under LGPL - original licence link has changed is not relivant.
16259  *
16260  * Fork - LGPL
16261  * <script type="text/javascript">
16262  */
16263
16264 /**
16265  * @class Roo.state.Provider
16266  * Abstract base class for state provider implementations. This class provides methods
16267  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16268  * Provider interface.
16269  */
16270 Roo.state.Provider = function(){
16271     /**
16272      * @event statechange
16273      * Fires when a state change occurs.
16274      * @param {Provider} this This state provider
16275      * @param {String} key The state key which was changed
16276      * @param {String} value The encoded value for the state
16277      */
16278     this.addEvents({
16279         "statechange": true
16280     });
16281     this.state = {};
16282     Roo.state.Provider.superclass.constructor.call(this);
16283 };
16284 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16285     /**
16286      * Returns the current value for a key
16287      * @param {String} name The key name
16288      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16289      * @return {Mixed} The state data
16290      */
16291     get : function(name, defaultValue){
16292         return typeof this.state[name] == "undefined" ?
16293             defaultValue : this.state[name];
16294     },
16295     
16296     /**
16297      * Clears a value from the state
16298      * @param {String} name The key name
16299      */
16300     clear : function(name){
16301         delete this.state[name];
16302         this.fireEvent("statechange", this, name, null);
16303     },
16304     
16305     /**
16306      * Sets the value for a key
16307      * @param {String} name The key name
16308      * @param {Mixed} value The value to set
16309      */
16310     set : function(name, value){
16311         this.state[name] = value;
16312         this.fireEvent("statechange", this, name, value);
16313     },
16314     
16315     /**
16316      * Decodes a string previously encoded with {@link #encodeValue}.
16317      * @param {String} value The value to decode
16318      * @return {Mixed} The decoded value
16319      */
16320     decodeValue : function(cookie){
16321         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16322         var matches = re.exec(unescape(cookie));
16323         if(!matches || !matches[1]) {
16324             return; // non state cookie
16325         }
16326         var type = matches[1];
16327         var v = matches[2];
16328         switch(type){
16329             case "n":
16330                 return parseFloat(v);
16331             case "d":
16332                 return new Date(Date.parse(v));
16333             case "b":
16334                 return (v == "1");
16335             case "a":
16336                 var all = [];
16337                 var values = v.split("^");
16338                 for(var i = 0, len = values.length; i < len; i++){
16339                     all.push(this.decodeValue(values[i]));
16340                 }
16341                 return all;
16342            case "o":
16343                 var all = {};
16344                 var values = v.split("^");
16345                 for(var i = 0, len = values.length; i < len; i++){
16346                     var kv = values[i].split("=");
16347                     all[kv[0]] = this.decodeValue(kv[1]);
16348                 }
16349                 return all;
16350            default:
16351                 return v;
16352         }
16353     },
16354     
16355     /**
16356      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16357      * @param {Mixed} value The value to encode
16358      * @return {String} The encoded value
16359      */
16360     encodeValue : function(v){
16361         var enc;
16362         if(typeof v == "number"){
16363             enc = "n:" + v;
16364         }else if(typeof v == "boolean"){
16365             enc = "b:" + (v ? "1" : "0");
16366         }else if(v instanceof Date){
16367             enc = "d:" + v.toGMTString();
16368         }else if(v instanceof Array){
16369             var flat = "";
16370             for(var i = 0, len = v.length; i < len; i++){
16371                 flat += this.encodeValue(v[i]);
16372                 if(i != len-1) {
16373                     flat += "^";
16374                 }
16375             }
16376             enc = "a:" + flat;
16377         }else if(typeof v == "object"){
16378             var flat = "";
16379             for(var key in v){
16380                 if(typeof v[key] != "function"){
16381                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16382                 }
16383             }
16384             enc = "o:" + flat.substring(0, flat.length-1);
16385         }else{
16386             enc = "s:" + v;
16387         }
16388         return escape(enc);        
16389     }
16390 });
16391
16392 /*
16393  * Based on:
16394  * Ext JS Library 1.1.1
16395  * Copyright(c) 2006-2007, Ext JS, LLC.
16396  *
16397  * Originally Released Under LGPL - original licence link has changed is not relivant.
16398  *
16399  * Fork - LGPL
16400  * <script type="text/javascript">
16401  */
16402 /**
16403  * @class Roo.state.Manager
16404  * This is the global state manager. By default all components that are "state aware" check this class
16405  * for state information if you don't pass them a custom state provider. In order for this class
16406  * to be useful, it must be initialized with a provider when your application initializes.
16407  <pre><code>
16408 // in your initialization function
16409 init : function(){
16410    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16411    ...
16412    // supposed you have a {@link Roo.BorderLayout}
16413    var layout = new Roo.BorderLayout(...);
16414    layout.restoreState();
16415    // or a {Roo.BasicDialog}
16416    var dialog = new Roo.BasicDialog(...);
16417    dialog.restoreState();
16418  </code></pre>
16419  * @static
16420  */
16421 Roo.state.Manager = function(){
16422     var provider = new Roo.state.Provider();
16423     
16424     return {
16425         /**
16426          * Configures the default state provider for your application
16427          * @param {Provider} stateProvider The state provider to set
16428          */
16429         setProvider : function(stateProvider){
16430             provider = stateProvider;
16431         },
16432         
16433         /**
16434          * Returns the current value for a key
16435          * @param {String} name The key name
16436          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16437          * @return {Mixed} The state data
16438          */
16439         get : function(key, defaultValue){
16440             return provider.get(key, defaultValue);
16441         },
16442         
16443         /**
16444          * Sets the value for a key
16445          * @param {String} name The key name
16446          * @param {Mixed} value The state data
16447          */
16448          set : function(key, value){
16449             provider.set(key, value);
16450         },
16451         
16452         /**
16453          * Clears a value from the state
16454          * @param {String} name The key name
16455          */
16456         clear : function(key){
16457             provider.clear(key);
16458         },
16459         
16460         /**
16461          * Gets the currently configured state provider
16462          * @return {Provider} The state provider
16463          */
16464         getProvider : function(){
16465             return provider;
16466         }
16467     };
16468 }();
16469 /*
16470  * Based on:
16471  * Ext JS Library 1.1.1
16472  * Copyright(c) 2006-2007, Ext JS, LLC.
16473  *
16474  * Originally Released Under LGPL - original licence link has changed is not relivant.
16475  *
16476  * Fork - LGPL
16477  * <script type="text/javascript">
16478  */
16479 /**
16480  * @class Roo.state.CookieProvider
16481  * @extends Roo.state.Provider
16482  * The default Provider implementation which saves state via cookies.
16483  * <br />Usage:
16484  <pre><code>
16485    var cp = new Roo.state.CookieProvider({
16486        path: "/cgi-bin/",
16487        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16488        domain: "roojs.com"
16489    })
16490    Roo.state.Manager.setProvider(cp);
16491  </code></pre>
16492  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16493  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16494  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16495  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16496  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16497  * domain the page is running on including the 'www' like 'www.roojs.com')
16498  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16499  * @constructor
16500  * Create a new CookieProvider
16501  * @param {Object} config The configuration object
16502  */
16503 Roo.state.CookieProvider = function(config){
16504     Roo.state.CookieProvider.superclass.constructor.call(this);
16505     this.path = "/";
16506     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16507     this.domain = null;
16508     this.secure = false;
16509     Roo.apply(this, config);
16510     this.state = this.readCookies();
16511 };
16512
16513 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16514     // private
16515     set : function(name, value){
16516         if(typeof value == "undefined" || value === null){
16517             this.clear(name);
16518             return;
16519         }
16520         this.setCookie(name, value);
16521         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16522     },
16523
16524     // private
16525     clear : function(name){
16526         this.clearCookie(name);
16527         Roo.state.CookieProvider.superclass.clear.call(this, name);
16528     },
16529
16530     // private
16531     readCookies : function(){
16532         var cookies = {};
16533         var c = document.cookie + ";";
16534         var re = /\s?(.*?)=(.*?);/g;
16535         var matches;
16536         while((matches = re.exec(c)) != null){
16537             var name = matches[1];
16538             var value = matches[2];
16539             if(name && name.substring(0,3) == "ys-"){
16540                 cookies[name.substr(3)] = this.decodeValue(value);
16541             }
16542         }
16543         return cookies;
16544     },
16545
16546     // private
16547     setCookie : function(name, value){
16548         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16549            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16550            ((this.path == null) ? "" : ("; path=" + this.path)) +
16551            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16552            ((this.secure == true) ? "; secure" : "");
16553     },
16554
16555     // private
16556     clearCookie : function(name){
16557         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16558            ((this.path == null) ? "" : ("; path=" + this.path)) +
16559            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16560            ((this.secure == true) ? "; secure" : "");
16561     }
16562 });/*
16563  * Based on:
16564  * Ext JS Library 1.1.1
16565  * Copyright(c) 2006-2007, Ext JS, LLC.
16566  *
16567  * Originally Released Under LGPL - original licence link has changed is not relivant.
16568  *
16569  * Fork - LGPL
16570  * <script type="text/javascript">
16571  */
16572  
16573
16574 /**
16575  * @class Roo.ComponentMgr
16576  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16577  * @static
16578  */
16579 Roo.ComponentMgr = function(){
16580     var all = new Roo.util.MixedCollection();
16581
16582     return {
16583         /**
16584          * Registers a component.
16585          * @param {Roo.Component} c The component
16586          */
16587         register : function(c){
16588             all.add(c);
16589         },
16590
16591         /**
16592          * Unregisters a component.
16593          * @param {Roo.Component} c The component
16594          */
16595         unregister : function(c){
16596             all.remove(c);
16597         },
16598
16599         /**
16600          * Returns a component by id
16601          * @param {String} id The component id
16602          */
16603         get : function(id){
16604             return all.get(id);
16605         },
16606
16607         /**
16608          * Registers a function that will be called when a specified component is added to ComponentMgr
16609          * @param {String} id The component id
16610          * @param {Funtction} fn The callback function
16611          * @param {Object} scope The scope of the callback
16612          */
16613         onAvailable : function(id, fn, scope){
16614             all.on("add", function(index, o){
16615                 if(o.id == id){
16616                     fn.call(scope || o, o);
16617                     all.un("add", fn, scope);
16618                 }
16619             });
16620         }
16621     };
16622 }();/*
16623  * Based on:
16624  * Ext JS Library 1.1.1
16625  * Copyright(c) 2006-2007, Ext JS, LLC.
16626  *
16627  * Originally Released Under LGPL - original licence link has changed is not relivant.
16628  *
16629  * Fork - LGPL
16630  * <script type="text/javascript">
16631  */
16632  
16633 /**
16634  * @class Roo.Component
16635  * @extends Roo.util.Observable
16636  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16637  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16638  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16639  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16640  * All visual components (widgets) that require rendering into a layout should subclass Component.
16641  * @constructor
16642  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16643  * 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
16644  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16645  */
16646 Roo.Component = function(config){
16647     config = config || {};
16648     if(config.tagName || config.dom || typeof config == "string"){ // element object
16649         config = {el: config, id: config.id || config};
16650     }
16651     this.initialConfig = config;
16652
16653     Roo.apply(this, config);
16654     this.addEvents({
16655         /**
16656          * @event disable
16657          * Fires after the component is disabled.
16658              * @param {Roo.Component} this
16659              */
16660         disable : true,
16661         /**
16662          * @event enable
16663          * Fires after the component is enabled.
16664              * @param {Roo.Component} this
16665              */
16666         enable : true,
16667         /**
16668          * @event beforeshow
16669          * Fires before the component is shown.  Return false to stop the show.
16670              * @param {Roo.Component} this
16671              */
16672         beforeshow : true,
16673         /**
16674          * @event show
16675          * Fires after the component is shown.
16676              * @param {Roo.Component} this
16677              */
16678         show : true,
16679         /**
16680          * @event beforehide
16681          * Fires before the component is hidden. Return false to stop the hide.
16682              * @param {Roo.Component} this
16683              */
16684         beforehide : true,
16685         /**
16686          * @event hide
16687          * Fires after the component is hidden.
16688              * @param {Roo.Component} this
16689              */
16690         hide : true,
16691         /**
16692          * @event beforerender
16693          * Fires before the component is rendered. Return false to stop the render.
16694              * @param {Roo.Component} this
16695              */
16696         beforerender : true,
16697         /**
16698          * @event render
16699          * Fires after the component is rendered.
16700              * @param {Roo.Component} this
16701              */
16702         render : true,
16703         /**
16704          * @event beforedestroy
16705          * Fires before the component is destroyed. Return false to stop the destroy.
16706              * @param {Roo.Component} this
16707              */
16708         beforedestroy : true,
16709         /**
16710          * @event destroy
16711          * Fires after the component is destroyed.
16712              * @param {Roo.Component} this
16713              */
16714         destroy : true
16715     });
16716     if(!this.id){
16717         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16718     }
16719     Roo.ComponentMgr.register(this);
16720     Roo.Component.superclass.constructor.call(this);
16721     this.initComponent();
16722     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16723         this.render(this.renderTo);
16724         delete this.renderTo;
16725     }
16726 };
16727
16728 /** @private */
16729 Roo.Component.AUTO_ID = 1000;
16730
16731 Roo.extend(Roo.Component, Roo.util.Observable, {
16732     /**
16733      * @scope Roo.Component.prototype
16734      * @type {Boolean}
16735      * true if this component is hidden. Read-only.
16736      */
16737     hidden : false,
16738     /**
16739      * @type {Boolean}
16740      * true if this component is disabled. Read-only.
16741      */
16742     disabled : false,
16743     /**
16744      * @type {Boolean}
16745      * true if this component has been rendered. Read-only.
16746      */
16747     rendered : false,
16748     
16749     /** @cfg {String} disableClass
16750      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16751      */
16752     disabledClass : "x-item-disabled",
16753         /** @cfg {Boolean} allowDomMove
16754          * Whether the component can move the Dom node when rendering (defaults to true).
16755          */
16756     allowDomMove : true,
16757     /** @cfg {String} hideMode (display|visibility)
16758      * How this component should hidden. Supported values are
16759      * "visibility" (css visibility), "offsets" (negative offset position) and
16760      * "display" (css display) - defaults to "display".
16761      */
16762     hideMode: 'display',
16763
16764     /** @private */
16765     ctype : "Roo.Component",
16766
16767     /**
16768      * @cfg {String} actionMode 
16769      * which property holds the element that used for  hide() / show() / disable() / enable()
16770      * default is 'el' for forms you probably want to set this to fieldEl 
16771      */
16772     actionMode : "el",
16773
16774     /** @private */
16775     getActionEl : function(){
16776         return this[this.actionMode];
16777     },
16778
16779     initComponent : Roo.emptyFn,
16780     /**
16781      * If this is a lazy rendering component, render it to its container element.
16782      * @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.
16783      */
16784     render : function(container, position){
16785         
16786         if(this.rendered){
16787             return this;
16788         }
16789         
16790         if(this.fireEvent("beforerender", this) === false){
16791             return false;
16792         }
16793         
16794         if(!container && this.el){
16795             this.el = Roo.get(this.el);
16796             container = this.el.dom.parentNode;
16797             this.allowDomMove = false;
16798         }
16799         this.container = Roo.get(container);
16800         this.rendered = true;
16801         if(position !== undefined){
16802             if(typeof position == 'number'){
16803                 position = this.container.dom.childNodes[position];
16804             }else{
16805                 position = Roo.getDom(position);
16806             }
16807         }
16808         this.onRender(this.container, position || null);
16809         if(this.cls){
16810             this.el.addClass(this.cls);
16811             delete this.cls;
16812         }
16813         if(this.style){
16814             this.el.applyStyles(this.style);
16815             delete this.style;
16816         }
16817         this.fireEvent("render", this);
16818         this.afterRender(this.container);
16819         if(this.hidden){
16820             this.hide();
16821         }
16822         if(this.disabled){
16823             this.disable();
16824         }
16825
16826         return this;
16827         
16828     },
16829
16830     /** @private */
16831     // default function is not really useful
16832     onRender : function(ct, position){
16833         if(this.el){
16834             this.el = Roo.get(this.el);
16835             if(this.allowDomMove !== false){
16836                 ct.dom.insertBefore(this.el.dom, position);
16837             }
16838         }
16839     },
16840
16841     /** @private */
16842     getAutoCreate : function(){
16843         var cfg = typeof this.autoCreate == "object" ?
16844                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16845         if(this.id && !cfg.id){
16846             cfg.id = this.id;
16847         }
16848         return cfg;
16849     },
16850
16851     /** @private */
16852     afterRender : Roo.emptyFn,
16853
16854     /**
16855      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16856      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16857      */
16858     destroy : function(){
16859         if(this.fireEvent("beforedestroy", this) !== false){
16860             this.purgeListeners();
16861             this.beforeDestroy();
16862             if(this.rendered){
16863                 this.el.removeAllListeners();
16864                 this.el.remove();
16865                 if(this.actionMode == "container"){
16866                     this.container.remove();
16867                 }
16868             }
16869             this.onDestroy();
16870             Roo.ComponentMgr.unregister(this);
16871             this.fireEvent("destroy", this);
16872         }
16873     },
16874
16875         /** @private */
16876     beforeDestroy : function(){
16877
16878     },
16879
16880         /** @private */
16881         onDestroy : function(){
16882
16883     },
16884
16885     /**
16886      * Returns the underlying {@link Roo.Element}.
16887      * @return {Roo.Element} The element
16888      */
16889     getEl : function(){
16890         return this.el;
16891     },
16892
16893     /**
16894      * Returns the id of this component.
16895      * @return {String}
16896      */
16897     getId : function(){
16898         return this.id;
16899     },
16900
16901     /**
16902      * Try to focus this component.
16903      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16904      * @return {Roo.Component} this
16905      */
16906     focus : function(selectText){
16907         if(this.rendered){
16908             this.el.focus();
16909             if(selectText === true){
16910                 this.el.dom.select();
16911             }
16912         }
16913         return this;
16914     },
16915
16916     /** @private */
16917     blur : function(){
16918         if(this.rendered){
16919             this.el.blur();
16920         }
16921         return this;
16922     },
16923
16924     /**
16925      * Disable this component.
16926      * @return {Roo.Component} this
16927      */
16928     disable : function(){
16929         if(this.rendered){
16930             this.onDisable();
16931         }
16932         this.disabled = true;
16933         this.fireEvent("disable", this);
16934         return this;
16935     },
16936
16937         // private
16938     onDisable : function(){
16939         this.getActionEl().addClass(this.disabledClass);
16940         this.el.dom.disabled = true;
16941     },
16942
16943     /**
16944      * Enable this component.
16945      * @return {Roo.Component} this
16946      */
16947     enable : function(){
16948         if(this.rendered){
16949             this.onEnable();
16950         }
16951         this.disabled = false;
16952         this.fireEvent("enable", this);
16953         return this;
16954     },
16955
16956         // private
16957     onEnable : function(){
16958         this.getActionEl().removeClass(this.disabledClass);
16959         this.el.dom.disabled = false;
16960     },
16961
16962     /**
16963      * Convenience function for setting disabled/enabled by boolean.
16964      * @param {Boolean} disabled
16965      */
16966     setDisabled : function(disabled){
16967         this[disabled ? "disable" : "enable"]();
16968     },
16969
16970     /**
16971      * Show this component.
16972      * @return {Roo.Component} this
16973      */
16974     show: function(){
16975         if(this.fireEvent("beforeshow", this) !== false){
16976             this.hidden = false;
16977             if(this.rendered){
16978                 this.onShow();
16979             }
16980             this.fireEvent("show", this);
16981         }
16982         return this;
16983     },
16984
16985     // private
16986     onShow : function(){
16987         var ae = this.getActionEl();
16988         if(this.hideMode == 'visibility'){
16989             ae.dom.style.visibility = "visible";
16990         }else if(this.hideMode == 'offsets'){
16991             ae.removeClass('x-hidden');
16992         }else{
16993             ae.dom.style.display = "";
16994         }
16995     },
16996
16997     /**
16998      * Hide this component.
16999      * @return {Roo.Component} this
17000      */
17001     hide: function(){
17002         if(this.fireEvent("beforehide", this) !== false){
17003             this.hidden = true;
17004             if(this.rendered){
17005                 this.onHide();
17006             }
17007             this.fireEvent("hide", this);
17008         }
17009         return this;
17010     },
17011
17012     // private
17013     onHide : function(){
17014         var ae = this.getActionEl();
17015         if(this.hideMode == 'visibility'){
17016             ae.dom.style.visibility = "hidden";
17017         }else if(this.hideMode == 'offsets'){
17018             ae.addClass('x-hidden');
17019         }else{
17020             ae.dom.style.display = "none";
17021         }
17022     },
17023
17024     /**
17025      * Convenience function to hide or show this component by boolean.
17026      * @param {Boolean} visible True to show, false to hide
17027      * @return {Roo.Component} this
17028      */
17029     setVisible: function(visible){
17030         if(visible) {
17031             this.show();
17032         }else{
17033             this.hide();
17034         }
17035         return this;
17036     },
17037
17038     /**
17039      * Returns true if this component is visible.
17040      */
17041     isVisible : function(){
17042         return this.getActionEl().isVisible();
17043     },
17044
17045     cloneConfig : function(overrides){
17046         overrides = overrides || {};
17047         var id = overrides.id || Roo.id();
17048         var cfg = Roo.applyIf(overrides, this.initialConfig);
17049         cfg.id = id; // prevent dup id
17050         return new this.constructor(cfg);
17051     }
17052 });/*
17053  * Based on:
17054  * Ext JS Library 1.1.1
17055  * Copyright(c) 2006-2007, Ext JS, LLC.
17056  *
17057  * Originally Released Under LGPL - original licence link has changed is not relivant.
17058  *
17059  * Fork - LGPL
17060  * <script type="text/javascript">
17061  */
17062
17063 /**
17064  * @class Roo.BoxComponent
17065  * @extends Roo.Component
17066  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17067  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17068  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17069  * layout containers.
17070  * @constructor
17071  * @param {Roo.Element/String/Object} config The configuration options.
17072  */
17073 Roo.BoxComponent = function(config){
17074     Roo.Component.call(this, config);
17075     this.addEvents({
17076         /**
17077          * @event resize
17078          * Fires after the component is resized.
17079              * @param {Roo.Component} this
17080              * @param {Number} adjWidth The box-adjusted width that was set
17081              * @param {Number} adjHeight The box-adjusted height that was set
17082              * @param {Number} rawWidth The width that was originally specified
17083              * @param {Number} rawHeight The height that was originally specified
17084              */
17085         resize : true,
17086         /**
17087          * @event move
17088          * Fires after the component is moved.
17089              * @param {Roo.Component} this
17090              * @param {Number} x The new x position
17091              * @param {Number} y The new y position
17092              */
17093         move : true
17094     });
17095 };
17096
17097 Roo.extend(Roo.BoxComponent, Roo.Component, {
17098     // private, set in afterRender to signify that the component has been rendered
17099     boxReady : false,
17100     // private, used to defer height settings to subclasses
17101     deferHeight: false,
17102     /** @cfg {Number} width
17103      * width (optional) size of component
17104      */
17105      /** @cfg {Number} height
17106      * height (optional) size of component
17107      */
17108      
17109     /**
17110      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17111      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17112      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17113      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17114      * @return {Roo.BoxComponent} this
17115      */
17116     setSize : function(w, h){
17117         // support for standard size objects
17118         if(typeof w == 'object'){
17119             h = w.height;
17120             w = w.width;
17121         }
17122         // not rendered
17123         if(!this.boxReady){
17124             this.width = w;
17125             this.height = h;
17126             return this;
17127         }
17128
17129         // prevent recalcs when not needed
17130         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17131             return this;
17132         }
17133         this.lastSize = {width: w, height: h};
17134
17135         var adj = this.adjustSize(w, h);
17136         var aw = adj.width, ah = adj.height;
17137         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17138             var rz = this.getResizeEl();
17139             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17140                 rz.setSize(aw, ah);
17141             }else if(!this.deferHeight && ah !== undefined){
17142                 rz.setHeight(ah);
17143             }else if(aw !== undefined){
17144                 rz.setWidth(aw);
17145             }
17146             this.onResize(aw, ah, w, h);
17147             this.fireEvent('resize', this, aw, ah, w, h);
17148         }
17149         return this;
17150     },
17151
17152     /**
17153      * Gets the current size of the component's underlying element.
17154      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17155      */
17156     getSize : function(){
17157         return this.el.getSize();
17158     },
17159
17160     /**
17161      * Gets the current XY position of the component's underlying element.
17162      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17163      * @return {Array} The XY position of the element (e.g., [100, 200])
17164      */
17165     getPosition : function(local){
17166         if(local === true){
17167             return [this.el.getLeft(true), this.el.getTop(true)];
17168         }
17169         return this.xy || this.el.getXY();
17170     },
17171
17172     /**
17173      * Gets the current box measurements of the component's underlying element.
17174      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17175      * @returns {Object} box An object in the format {x, y, width, height}
17176      */
17177     getBox : function(local){
17178         var s = this.el.getSize();
17179         if(local){
17180             s.x = this.el.getLeft(true);
17181             s.y = this.el.getTop(true);
17182         }else{
17183             var xy = this.xy || this.el.getXY();
17184             s.x = xy[0];
17185             s.y = xy[1];
17186         }
17187         return s;
17188     },
17189
17190     /**
17191      * Sets the current box measurements of the component's underlying element.
17192      * @param {Object} box An object in the format {x, y, width, height}
17193      * @returns {Roo.BoxComponent} this
17194      */
17195     updateBox : function(box){
17196         this.setSize(box.width, box.height);
17197         this.setPagePosition(box.x, box.y);
17198         return this;
17199     },
17200
17201     // protected
17202     getResizeEl : function(){
17203         return this.resizeEl || this.el;
17204     },
17205
17206     // protected
17207     getPositionEl : function(){
17208         return this.positionEl || this.el;
17209     },
17210
17211     /**
17212      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17213      * This method fires the move event.
17214      * @param {Number} left The new left
17215      * @param {Number} top The new top
17216      * @returns {Roo.BoxComponent} this
17217      */
17218     setPosition : function(x, y){
17219         this.x = x;
17220         this.y = y;
17221         if(!this.boxReady){
17222             return this;
17223         }
17224         var adj = this.adjustPosition(x, y);
17225         var ax = adj.x, ay = adj.y;
17226
17227         var el = this.getPositionEl();
17228         if(ax !== undefined || ay !== undefined){
17229             if(ax !== undefined && ay !== undefined){
17230                 el.setLeftTop(ax, ay);
17231             }else if(ax !== undefined){
17232                 el.setLeft(ax);
17233             }else if(ay !== undefined){
17234                 el.setTop(ay);
17235             }
17236             this.onPosition(ax, ay);
17237             this.fireEvent('move', this, ax, ay);
17238         }
17239         return this;
17240     },
17241
17242     /**
17243      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17244      * This method fires the move event.
17245      * @param {Number} x The new x position
17246      * @param {Number} y The new y position
17247      * @returns {Roo.BoxComponent} this
17248      */
17249     setPagePosition : function(x, y){
17250         this.pageX = x;
17251         this.pageY = y;
17252         if(!this.boxReady){
17253             return;
17254         }
17255         if(x === undefined || y === undefined){ // cannot translate undefined points
17256             return;
17257         }
17258         var p = this.el.translatePoints(x, y);
17259         this.setPosition(p.left, p.top);
17260         return this;
17261     },
17262
17263     // private
17264     onRender : function(ct, position){
17265         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17266         if(this.resizeEl){
17267             this.resizeEl = Roo.get(this.resizeEl);
17268         }
17269         if(this.positionEl){
17270             this.positionEl = Roo.get(this.positionEl);
17271         }
17272     },
17273
17274     // private
17275     afterRender : function(){
17276         Roo.BoxComponent.superclass.afterRender.call(this);
17277         this.boxReady = true;
17278         this.setSize(this.width, this.height);
17279         if(this.x || this.y){
17280             this.setPosition(this.x, this.y);
17281         }
17282         if(this.pageX || this.pageY){
17283             this.setPagePosition(this.pageX, this.pageY);
17284         }
17285     },
17286
17287     /**
17288      * Force the component's size to recalculate based on the underlying element's current height and width.
17289      * @returns {Roo.BoxComponent} this
17290      */
17291     syncSize : function(){
17292         delete this.lastSize;
17293         this.setSize(this.el.getWidth(), this.el.getHeight());
17294         return this;
17295     },
17296
17297     /**
17298      * Called after the component is resized, this method is empty by default but can be implemented by any
17299      * subclass that needs to perform custom logic after a resize occurs.
17300      * @param {Number} adjWidth The box-adjusted width that was set
17301      * @param {Number} adjHeight The box-adjusted height that was set
17302      * @param {Number} rawWidth The width that was originally specified
17303      * @param {Number} rawHeight The height that was originally specified
17304      */
17305     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17306
17307     },
17308
17309     /**
17310      * Called after the component is moved, this method is empty by default but can be implemented by any
17311      * subclass that needs to perform custom logic after a move occurs.
17312      * @param {Number} x The new x position
17313      * @param {Number} y The new y position
17314      */
17315     onPosition : function(x, y){
17316
17317     },
17318
17319     // private
17320     adjustSize : function(w, h){
17321         if(this.autoWidth){
17322             w = 'auto';
17323         }
17324         if(this.autoHeight){
17325             h = 'auto';
17326         }
17327         return {width : w, height: h};
17328     },
17329
17330     // private
17331     adjustPosition : function(x, y){
17332         return {x : x, y: y};
17333     }
17334 });/*
17335  * Based on:
17336  * Ext JS Library 1.1.1
17337  * Copyright(c) 2006-2007, Ext JS, LLC.
17338  *
17339  * Originally Released Under LGPL - original licence link has changed is not relivant.
17340  *
17341  * Fork - LGPL
17342  * <script type="text/javascript">
17343  */
17344  (function(){ 
17345 /**
17346  * @class Roo.Layer
17347  * @extends Roo.Element
17348  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17349  * automatic maintaining of shadow/shim positions.
17350  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17351  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17352  * you can pass a string with a CSS class name. False turns off the shadow.
17353  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17354  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17355  * @cfg {String} cls CSS class to add to the element
17356  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17357  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17358  * @constructor
17359  * @param {Object} config An object with config options.
17360  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17361  */
17362
17363 Roo.Layer = function(config, existingEl){
17364     config = config || {};
17365     var dh = Roo.DomHelper;
17366     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17367     if(existingEl){
17368         this.dom = Roo.getDom(existingEl);
17369     }
17370     if(!this.dom){
17371         var o = config.dh || {tag: "div", cls: "x-layer"};
17372         this.dom = dh.append(pel, o);
17373     }
17374     if(config.cls){
17375         this.addClass(config.cls);
17376     }
17377     this.constrain = config.constrain !== false;
17378     this.visibilityMode = Roo.Element.VISIBILITY;
17379     if(config.id){
17380         this.id = this.dom.id = config.id;
17381     }else{
17382         this.id = Roo.id(this.dom);
17383     }
17384     this.zindex = config.zindex || this.getZIndex();
17385     this.position("absolute", this.zindex);
17386     if(config.shadow){
17387         this.shadowOffset = config.shadowOffset || 4;
17388         this.shadow = new Roo.Shadow({
17389             offset : this.shadowOffset,
17390             mode : config.shadow
17391         });
17392     }else{
17393         this.shadowOffset = 0;
17394     }
17395     this.useShim = config.shim !== false && Roo.useShims;
17396     this.useDisplay = config.useDisplay;
17397     this.hide();
17398 };
17399
17400 var supr = Roo.Element.prototype;
17401
17402 // shims are shared among layer to keep from having 100 iframes
17403 var shims = [];
17404
17405 Roo.extend(Roo.Layer, Roo.Element, {
17406
17407     getZIndex : function(){
17408         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17409     },
17410
17411     getShim : function(){
17412         if(!this.useShim){
17413             return null;
17414         }
17415         if(this.shim){
17416             return this.shim;
17417         }
17418         var shim = shims.shift();
17419         if(!shim){
17420             shim = this.createShim();
17421             shim.enableDisplayMode('block');
17422             shim.dom.style.display = 'none';
17423             shim.dom.style.visibility = 'visible';
17424         }
17425         var pn = this.dom.parentNode;
17426         if(shim.dom.parentNode != pn){
17427             pn.insertBefore(shim.dom, this.dom);
17428         }
17429         shim.setStyle('z-index', this.getZIndex()-2);
17430         this.shim = shim;
17431         return shim;
17432     },
17433
17434     hideShim : function(){
17435         if(this.shim){
17436             this.shim.setDisplayed(false);
17437             shims.push(this.shim);
17438             delete this.shim;
17439         }
17440     },
17441
17442     disableShadow : function(){
17443         if(this.shadow){
17444             this.shadowDisabled = true;
17445             this.shadow.hide();
17446             this.lastShadowOffset = this.shadowOffset;
17447             this.shadowOffset = 0;
17448         }
17449     },
17450
17451     enableShadow : function(show){
17452         if(this.shadow){
17453             this.shadowDisabled = false;
17454             this.shadowOffset = this.lastShadowOffset;
17455             delete this.lastShadowOffset;
17456             if(show){
17457                 this.sync(true);
17458             }
17459         }
17460     },
17461
17462     // private
17463     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17464     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17465     sync : function(doShow){
17466         var sw = this.shadow;
17467         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17468             var sh = this.getShim();
17469
17470             var w = this.getWidth(),
17471                 h = this.getHeight();
17472
17473             var l = this.getLeft(true),
17474                 t = this.getTop(true);
17475
17476             if(sw && !this.shadowDisabled){
17477                 if(doShow && !sw.isVisible()){
17478                     sw.show(this);
17479                 }else{
17480                     sw.realign(l, t, w, h);
17481                 }
17482                 if(sh){
17483                     if(doShow){
17484                        sh.show();
17485                     }
17486                     // fit the shim behind the shadow, so it is shimmed too
17487                     var a = sw.adjusts, s = sh.dom.style;
17488                     s.left = (Math.min(l, l+a.l))+"px";
17489                     s.top = (Math.min(t, t+a.t))+"px";
17490                     s.width = (w+a.w)+"px";
17491                     s.height = (h+a.h)+"px";
17492                 }
17493             }else if(sh){
17494                 if(doShow){
17495                    sh.show();
17496                 }
17497                 sh.setSize(w, h);
17498                 sh.setLeftTop(l, t);
17499             }
17500             
17501         }
17502     },
17503
17504     // private
17505     destroy : function(){
17506         this.hideShim();
17507         if(this.shadow){
17508             this.shadow.hide();
17509         }
17510         this.removeAllListeners();
17511         var pn = this.dom.parentNode;
17512         if(pn){
17513             pn.removeChild(this.dom);
17514         }
17515         Roo.Element.uncache(this.id);
17516     },
17517
17518     remove : function(){
17519         this.destroy();
17520     },
17521
17522     // private
17523     beginUpdate : function(){
17524         this.updating = true;
17525     },
17526
17527     // private
17528     endUpdate : function(){
17529         this.updating = false;
17530         this.sync(true);
17531     },
17532
17533     // private
17534     hideUnders : function(negOffset){
17535         if(this.shadow){
17536             this.shadow.hide();
17537         }
17538         this.hideShim();
17539     },
17540
17541     // private
17542     constrainXY : function(){
17543         if(this.constrain){
17544             var vw = Roo.lib.Dom.getViewWidth(),
17545                 vh = Roo.lib.Dom.getViewHeight();
17546             var s = Roo.get(document).getScroll();
17547
17548             var xy = this.getXY();
17549             var x = xy[0], y = xy[1];   
17550             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17551             // only move it if it needs it
17552             var moved = false;
17553             // first validate right/bottom
17554             if((x + w) > vw+s.left){
17555                 x = vw - w - this.shadowOffset;
17556                 moved = true;
17557             }
17558             if((y + h) > vh+s.top){
17559                 y = vh - h - this.shadowOffset;
17560                 moved = true;
17561             }
17562             // then make sure top/left isn't negative
17563             if(x < s.left){
17564                 x = s.left;
17565                 moved = true;
17566             }
17567             if(y < s.top){
17568                 y = s.top;
17569                 moved = true;
17570             }
17571             if(moved){
17572                 if(this.avoidY){
17573                     var ay = this.avoidY;
17574                     if(y <= ay && (y+h) >= ay){
17575                         y = ay-h-5;   
17576                     }
17577                 }
17578                 xy = [x, y];
17579                 this.storeXY(xy);
17580                 supr.setXY.call(this, xy);
17581                 this.sync();
17582             }
17583         }
17584     },
17585
17586     isVisible : function(){
17587         return this.visible;    
17588     },
17589
17590     // private
17591     showAction : function(){
17592         this.visible = true; // track visibility to prevent getStyle calls
17593         if(this.useDisplay === true){
17594             this.setDisplayed("");
17595         }else if(this.lastXY){
17596             supr.setXY.call(this, this.lastXY);
17597         }else if(this.lastLT){
17598             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17599         }
17600     },
17601
17602     // private
17603     hideAction : function(){
17604         this.visible = false;
17605         if(this.useDisplay === true){
17606             this.setDisplayed(false);
17607         }else{
17608             this.setLeftTop(-10000,-10000);
17609         }
17610     },
17611
17612     // overridden Element method
17613     setVisible : function(v, a, d, c, e){
17614         if(v){
17615             this.showAction();
17616         }
17617         if(a && v){
17618             var cb = function(){
17619                 this.sync(true);
17620                 if(c){
17621                     c();
17622                 }
17623             }.createDelegate(this);
17624             supr.setVisible.call(this, true, true, d, cb, e);
17625         }else{
17626             if(!v){
17627                 this.hideUnders(true);
17628             }
17629             var cb = c;
17630             if(a){
17631                 cb = function(){
17632                     this.hideAction();
17633                     if(c){
17634                         c();
17635                     }
17636                 }.createDelegate(this);
17637             }
17638             supr.setVisible.call(this, v, a, d, cb, e);
17639             if(v){
17640                 this.sync(true);
17641             }else if(!a){
17642                 this.hideAction();
17643             }
17644         }
17645     },
17646
17647     storeXY : function(xy){
17648         delete this.lastLT;
17649         this.lastXY = xy;
17650     },
17651
17652     storeLeftTop : function(left, top){
17653         delete this.lastXY;
17654         this.lastLT = [left, top];
17655     },
17656
17657     // private
17658     beforeFx : function(){
17659         this.beforeAction();
17660         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17661     },
17662
17663     // private
17664     afterFx : function(){
17665         Roo.Layer.superclass.afterFx.apply(this, arguments);
17666         this.sync(this.isVisible());
17667     },
17668
17669     // private
17670     beforeAction : function(){
17671         if(!this.updating && this.shadow){
17672             this.shadow.hide();
17673         }
17674     },
17675
17676     // overridden Element method
17677     setLeft : function(left){
17678         this.storeLeftTop(left, this.getTop(true));
17679         supr.setLeft.apply(this, arguments);
17680         this.sync();
17681     },
17682
17683     setTop : function(top){
17684         this.storeLeftTop(this.getLeft(true), top);
17685         supr.setTop.apply(this, arguments);
17686         this.sync();
17687     },
17688
17689     setLeftTop : function(left, top){
17690         this.storeLeftTop(left, top);
17691         supr.setLeftTop.apply(this, arguments);
17692         this.sync();
17693     },
17694
17695     setXY : function(xy, a, d, c, e){
17696         this.fixDisplay();
17697         this.beforeAction();
17698         this.storeXY(xy);
17699         var cb = this.createCB(c);
17700         supr.setXY.call(this, xy, a, d, cb, e);
17701         if(!a){
17702             cb();
17703         }
17704     },
17705
17706     // private
17707     createCB : function(c){
17708         var el = this;
17709         return function(){
17710             el.constrainXY();
17711             el.sync(true);
17712             if(c){
17713                 c();
17714             }
17715         };
17716     },
17717
17718     // overridden Element method
17719     setX : function(x, a, d, c, e){
17720         this.setXY([x, this.getY()], a, d, c, e);
17721     },
17722
17723     // overridden Element method
17724     setY : function(y, a, d, c, e){
17725         this.setXY([this.getX(), y], a, d, c, e);
17726     },
17727
17728     // overridden Element method
17729     setSize : function(w, h, a, d, c, e){
17730         this.beforeAction();
17731         var cb = this.createCB(c);
17732         supr.setSize.call(this, w, h, a, d, cb, e);
17733         if(!a){
17734             cb();
17735         }
17736     },
17737
17738     // overridden Element method
17739     setWidth : function(w, a, d, c, e){
17740         this.beforeAction();
17741         var cb = this.createCB(c);
17742         supr.setWidth.call(this, w, a, d, cb, e);
17743         if(!a){
17744             cb();
17745         }
17746     },
17747
17748     // overridden Element method
17749     setHeight : function(h, a, d, c, e){
17750         this.beforeAction();
17751         var cb = this.createCB(c);
17752         supr.setHeight.call(this, h, a, d, cb, e);
17753         if(!a){
17754             cb();
17755         }
17756     },
17757
17758     // overridden Element method
17759     setBounds : function(x, y, w, h, a, d, c, e){
17760         this.beforeAction();
17761         var cb = this.createCB(c);
17762         if(!a){
17763             this.storeXY([x, y]);
17764             supr.setXY.call(this, [x, y]);
17765             supr.setSize.call(this, w, h, a, d, cb, e);
17766             cb();
17767         }else{
17768             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17769         }
17770         return this;
17771     },
17772     
17773     /**
17774      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17775      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17776      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17777      * @param {Number} zindex The new z-index to set
17778      * @return {this} The Layer
17779      */
17780     setZIndex : function(zindex){
17781         this.zindex = zindex;
17782         this.setStyle("z-index", zindex + 2);
17783         if(this.shadow){
17784             this.shadow.setZIndex(zindex + 1);
17785         }
17786         if(this.shim){
17787             this.shim.setStyle("z-index", zindex);
17788         }
17789     }
17790 });
17791 })();/*
17792  * Original code for Roojs - LGPL
17793  * <script type="text/javascript">
17794  */
17795  
17796 /**
17797  * @class Roo.XComponent
17798  * A delayed Element creator...
17799  * Or a way to group chunks of interface together.
17800  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17801  *  used in conjunction with XComponent.build() it will create an instance of each element,
17802  *  then call addxtype() to build the User interface.
17803  * 
17804  * Mypart.xyx = new Roo.XComponent({
17805
17806     parent : 'Mypart.xyz', // empty == document.element.!!
17807     order : '001',
17808     name : 'xxxx'
17809     region : 'xxxx'
17810     disabled : function() {} 
17811      
17812     tree : function() { // return an tree of xtype declared components
17813         var MODULE = this;
17814         return 
17815         {
17816             xtype : 'NestedLayoutPanel',
17817             // technicall
17818         }
17819      ]
17820  *})
17821  *
17822  *
17823  * It can be used to build a big heiracy, with parent etc.
17824  * or you can just use this to render a single compoent to a dom element
17825  * MYPART.render(Roo.Element | String(id) | dom_element )
17826  *
17827  *
17828  * Usage patterns.
17829  *
17830  * Classic Roo
17831  *
17832  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17833  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17834  *
17835  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17836  *
17837  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17838  * - if mulitple topModules exist, the last one is defined as the top module.
17839  *
17840  * Embeded Roo
17841  * 
17842  * When the top level or multiple modules are to embedded into a existing HTML page,
17843  * the parent element can container '#id' of the element where the module will be drawn.
17844  *
17845  * Bootstrap Roo
17846  *
17847  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17848  * it relies more on a include mechanism, where sub modules are included into an outer page.
17849  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17850  * 
17851  * Bootstrap Roo Included elements
17852  *
17853  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17854  * hence confusing the component builder as it thinks there are multiple top level elements. 
17855  *
17856  * String Over-ride & Translations
17857  *
17858  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17859  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17860  * are needed. @see Roo.XComponent.overlayString  
17861  * 
17862  * 
17863  * 
17864  * @extends Roo.util.Observable
17865  * @constructor
17866  * @param cfg {Object} configuration of component
17867  * 
17868  */
17869 Roo.XComponent = function(cfg) {
17870     Roo.apply(this, cfg);
17871     this.addEvents({ 
17872         /**
17873              * @event built
17874              * Fires when this the componnt is built
17875              * @param {Roo.XComponent} c the component
17876              */
17877         'built' : true
17878         
17879     });
17880     this.region = this.region || 'center'; // default..
17881     Roo.XComponent.register(this);
17882     this.modules = false;
17883     this.el = false; // where the layout goes..
17884     
17885     
17886 }
17887 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17888     /**
17889      * @property el
17890      * The created element (with Roo.factory())
17891      * @type {Roo.Layout}
17892      */
17893     el  : false,
17894     
17895     /**
17896      * @property el
17897      * for BC  - use el in new code
17898      * @type {Roo.Layout}
17899      */
17900     panel : false,
17901     
17902     /**
17903      * @property layout
17904      * for BC  - use el in new code
17905      * @type {Roo.Layout}
17906      */
17907     layout : false,
17908     
17909      /**
17910      * @cfg {Function|boolean} disabled
17911      * If this module is disabled by some rule, return true from the funtion
17912      */
17913     disabled : false,
17914     
17915     /**
17916      * @cfg {String} parent 
17917      * Name of parent element which it get xtype added to..
17918      */
17919     parent: false,
17920     
17921     /**
17922      * @cfg {String} order
17923      * Used to set the order in which elements are created (usefull for multiple tabs)
17924      */
17925     
17926     order : false,
17927     /**
17928      * @cfg {String} name
17929      * String to display while loading.
17930      */
17931     name : false,
17932     /**
17933      * @cfg {String} region
17934      * Region to render component to (defaults to center)
17935      */
17936     region : 'center',
17937     
17938     /**
17939      * @cfg {Array} items
17940      * A single item array - the first element is the root of the tree..
17941      * It's done this way to stay compatible with the Xtype system...
17942      */
17943     items : false,
17944     
17945     /**
17946      * @property _tree
17947      * The method that retuns the tree of parts that make up this compoennt 
17948      * @type {function}
17949      */
17950     _tree  : false,
17951     
17952      /**
17953      * render
17954      * render element to dom or tree
17955      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17956      */
17957     
17958     render : function(el)
17959     {
17960         
17961         el = el || false;
17962         var hp = this.parent ? 1 : 0;
17963         Roo.debug &&  Roo.log(this);
17964         
17965         var tree = this._tree ? this._tree() : this.tree();
17966
17967         
17968         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17969             // if parent is a '#.....' string, then let's use that..
17970             var ename = this.parent.substr(1);
17971             this.parent = false;
17972             Roo.debug && Roo.log(ename);
17973             switch (ename) {
17974                 case 'bootstrap-body':
17975                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17976                         // this is the BorderLayout standard?
17977                        this.parent = { el : true };
17978                        break;
17979                     }
17980                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17981                         // need to insert stuff...
17982                         this.parent =  {
17983                              el : new Roo.bootstrap.layout.Border({
17984                                  el : document.body, 
17985                      
17986                                  center: {
17987                                     titlebar: false,
17988                                     autoScroll:false,
17989                                     closeOnTab: true,
17990                                     tabPosition: 'top',
17991                                       //resizeTabs: true,
17992                                     alwaysShowTabs: true,
17993                                     hideTabs: false
17994                                      //minTabWidth: 140
17995                                  }
17996                              })
17997                         
17998                          };
17999                          break;
18000                     }
18001                          
18002                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18003                         this.parent = { el :  new  Roo.bootstrap.Body() };
18004                         Roo.debug && Roo.log("setting el to doc body");
18005                          
18006                     } else {
18007                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18008                     }
18009                     break;
18010                 case 'bootstrap':
18011                     this.parent = { el : true};
18012                     // fall through
18013                 default:
18014                     el = Roo.get(ename);
18015                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18016                         this.parent = { el : true};
18017                     }
18018                     
18019                     break;
18020             }
18021                 
18022             
18023             if (!el && !this.parent) {
18024                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18025                 return;
18026             }
18027         }
18028         
18029         Roo.debug && Roo.log("EL:");
18030         Roo.debug && Roo.log(el);
18031         Roo.debug && Roo.log("this.parent.el:");
18032         Roo.debug && Roo.log(this.parent.el);
18033         
18034
18035         // altertive root elements ??? - we need a better way to indicate these.
18036         var is_alt = Roo.XComponent.is_alt ||
18037                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18038                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18039                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18040         
18041         
18042         
18043         if (!this.parent && is_alt) {
18044             //el = Roo.get(document.body);
18045             this.parent = { el : true };
18046         }
18047             
18048             
18049         
18050         if (!this.parent) {
18051             
18052             Roo.debug && Roo.log("no parent - creating one");
18053             
18054             el = el ? Roo.get(el) : false;      
18055             
18056             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18057                 
18058                 this.parent =  {
18059                     el : new Roo.bootstrap.layout.Border({
18060                         el: el || document.body,
18061                     
18062                         center: {
18063                             titlebar: false,
18064                             autoScroll:false,
18065                             closeOnTab: true,
18066                             tabPosition: 'top',
18067                              //resizeTabs: true,
18068                             alwaysShowTabs: false,
18069                             hideTabs: true,
18070                             minTabWidth: 140,
18071                             overflow: 'visible'
18072                          }
18073                      })
18074                 };
18075             } else {
18076             
18077                 // it's a top level one..
18078                 this.parent =  {
18079                     el : new Roo.BorderLayout(el || document.body, {
18080                         center: {
18081                             titlebar: false,
18082                             autoScroll:false,
18083                             closeOnTab: true,
18084                             tabPosition: 'top',
18085                              //resizeTabs: true,
18086                             alwaysShowTabs: el && hp? false :  true,
18087                             hideTabs: el || !hp ? true :  false,
18088                             minTabWidth: 140
18089                          }
18090                     })
18091                 };
18092             }
18093         }
18094         
18095         if (!this.parent.el) {
18096                 // probably an old style ctor, which has been disabled.
18097                 return;
18098
18099         }
18100                 // The 'tree' method is  '_tree now' 
18101             
18102         tree.region = tree.region || this.region;
18103         var is_body = false;
18104         if (this.parent.el === true) {
18105             // bootstrap... - body..
18106             if (el) {
18107                 tree.el = el;
18108             }
18109             this.parent.el = Roo.factory(tree);
18110             is_body = true;
18111         }
18112         
18113         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18114         this.fireEvent('built', this);
18115         
18116         this.panel = this.el;
18117         this.layout = this.panel.layout;
18118         this.parentLayout = this.parent.layout  || false;  
18119          
18120     }
18121     
18122 });
18123
18124 Roo.apply(Roo.XComponent, {
18125     /**
18126      * @property  hideProgress
18127      * true to disable the building progress bar.. usefull on single page renders.
18128      * @type Boolean
18129      */
18130     hideProgress : false,
18131     /**
18132      * @property  buildCompleted
18133      * True when the builder has completed building the interface.
18134      * @type Boolean
18135      */
18136     buildCompleted : false,
18137      
18138     /**
18139      * @property  topModule
18140      * the upper most module - uses document.element as it's constructor.
18141      * @type Object
18142      */
18143      
18144     topModule  : false,
18145       
18146     /**
18147      * @property  modules
18148      * array of modules to be created by registration system.
18149      * @type {Array} of Roo.XComponent
18150      */
18151     
18152     modules : [],
18153     /**
18154      * @property  elmodules
18155      * array of modules to be created by which use #ID 
18156      * @type {Array} of Roo.XComponent
18157      */
18158      
18159     elmodules : [],
18160
18161      /**
18162      * @property  is_alt
18163      * Is an alternative Root - normally used by bootstrap or other systems,
18164      *    where the top element in the tree can wrap 'body' 
18165      * @type {boolean}  (default false)
18166      */
18167      
18168     is_alt : false,
18169     /**
18170      * @property  build_from_html
18171      * Build elements from html - used by bootstrap HTML stuff 
18172      *    - this is cleared after build is completed
18173      * @type {boolean}    (default false)
18174      */
18175      
18176     build_from_html : false,
18177     /**
18178      * Register components to be built later.
18179      *
18180      * This solves the following issues
18181      * - Building is not done on page load, but after an authentication process has occured.
18182      * - Interface elements are registered on page load
18183      * - Parent Interface elements may not be loaded before child, so this handles that..
18184      * 
18185      *
18186      * example:
18187      * 
18188      * MyApp.register({
18189           order : '000001',
18190           module : 'Pman.Tab.projectMgr',
18191           region : 'center',
18192           parent : 'Pman.layout',
18193           disabled : false,  // or use a function..
18194         })
18195      
18196      * * @param {Object} details about module
18197      */
18198     register : function(obj) {
18199                 
18200         Roo.XComponent.event.fireEvent('register', obj);
18201         switch(typeof(obj.disabled) ) {
18202                 
18203             case 'undefined':
18204                 break;
18205             
18206             case 'function':
18207                 if ( obj.disabled() ) {
18208                         return;
18209                 }
18210                 break;
18211             
18212             default:
18213                 if (obj.disabled || obj.region == '#disabled') {
18214                         return;
18215                 }
18216                 break;
18217         }
18218                 
18219         this.modules.push(obj);
18220          
18221     },
18222     /**
18223      * convert a string to an object..
18224      * eg. 'AAA.BBB' -> finds AAA.BBB
18225
18226      */
18227     
18228     toObject : function(str)
18229     {
18230         if (!str || typeof(str) == 'object') {
18231             return str;
18232         }
18233         if (str.substring(0,1) == '#') {
18234             return str;
18235         }
18236
18237         var ar = str.split('.');
18238         var rt, o;
18239         rt = ar.shift();
18240             /** eval:var:o */
18241         try {
18242             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18243         } catch (e) {
18244             throw "Module not found : " + str;
18245         }
18246         
18247         if (o === false) {
18248             throw "Module not found : " + str;
18249         }
18250         Roo.each(ar, function(e) {
18251             if (typeof(o[e]) == 'undefined') {
18252                 throw "Module not found : " + str;
18253             }
18254             o = o[e];
18255         });
18256         
18257         return o;
18258         
18259     },
18260     
18261     
18262     /**
18263      * move modules into their correct place in the tree..
18264      * 
18265      */
18266     preBuild : function ()
18267     {
18268         var _t = this;
18269         Roo.each(this.modules , function (obj)
18270         {
18271             Roo.XComponent.event.fireEvent('beforebuild', obj);
18272             
18273             var opar = obj.parent;
18274             try { 
18275                 obj.parent = this.toObject(opar);
18276             } catch(e) {
18277                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18278                 return;
18279             }
18280             
18281             if (!obj.parent) {
18282                 Roo.debug && Roo.log("GOT top level module");
18283                 Roo.debug && Roo.log(obj);
18284                 obj.modules = new Roo.util.MixedCollection(false, 
18285                     function(o) { return o.order + '' }
18286                 );
18287                 this.topModule = obj;
18288                 return;
18289             }
18290                         // parent is a string (usually a dom element name..)
18291             if (typeof(obj.parent) == 'string') {
18292                 this.elmodules.push(obj);
18293                 return;
18294             }
18295             if (obj.parent.constructor != Roo.XComponent) {
18296                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18297             }
18298             if (!obj.parent.modules) {
18299                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18300                     function(o) { return o.order + '' }
18301                 );
18302             }
18303             if (obj.parent.disabled) {
18304                 obj.disabled = true;
18305             }
18306             obj.parent.modules.add(obj);
18307         }, this);
18308     },
18309     
18310      /**
18311      * make a list of modules to build.
18312      * @return {Array} list of modules. 
18313      */ 
18314     
18315     buildOrder : function()
18316     {
18317         var _this = this;
18318         var cmp = function(a,b) {   
18319             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18320         };
18321         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18322             throw "No top level modules to build";
18323         }
18324         
18325         // make a flat list in order of modules to build.
18326         var mods = this.topModule ? [ this.topModule ] : [];
18327                 
18328         
18329         // elmodules (is a list of DOM based modules )
18330         Roo.each(this.elmodules, function(e) {
18331             mods.push(e);
18332             if (!this.topModule &&
18333                 typeof(e.parent) == 'string' &&
18334                 e.parent.substring(0,1) == '#' &&
18335                 Roo.get(e.parent.substr(1))
18336                ) {
18337                 
18338                 _this.topModule = e;
18339             }
18340             
18341         });
18342
18343         
18344         // add modules to their parents..
18345         var addMod = function(m) {
18346             Roo.debug && Roo.log("build Order: add: " + m.name);
18347                 
18348             mods.push(m);
18349             if (m.modules && !m.disabled) {
18350                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18351                 m.modules.keySort('ASC',  cmp );
18352                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18353     
18354                 m.modules.each(addMod);
18355             } else {
18356                 Roo.debug && Roo.log("build Order: no child modules");
18357             }
18358             // not sure if this is used any more..
18359             if (m.finalize) {
18360                 m.finalize.name = m.name + " (clean up) ";
18361                 mods.push(m.finalize);
18362             }
18363             
18364         }
18365         if (this.topModule && this.topModule.modules) { 
18366             this.topModule.modules.keySort('ASC',  cmp );
18367             this.topModule.modules.each(addMod);
18368         } 
18369         return mods;
18370     },
18371     
18372      /**
18373      * Build the registered modules.
18374      * @param {Object} parent element.
18375      * @param {Function} optional method to call after module has been added.
18376      * 
18377      */ 
18378    
18379     build : function(opts) 
18380     {
18381         
18382         if (typeof(opts) != 'undefined') {
18383             Roo.apply(this,opts);
18384         }
18385         
18386         this.preBuild();
18387         var mods = this.buildOrder();
18388       
18389         //this.allmods = mods;
18390         //Roo.debug && Roo.log(mods);
18391         //return;
18392         if (!mods.length) { // should not happen
18393             throw "NO modules!!!";
18394         }
18395         
18396         
18397         var msg = "Building Interface...";
18398         // flash it up as modal - so we store the mask!?
18399         if (!this.hideProgress && Roo.MessageBox) {
18400             Roo.MessageBox.show({ title: 'loading' });
18401             Roo.MessageBox.show({
18402                title: "Please wait...",
18403                msg: msg,
18404                width:450,
18405                progress:true,
18406                buttons : false,
18407                closable:false,
18408                modal: false
18409               
18410             });
18411         }
18412         var total = mods.length;
18413         
18414         var _this = this;
18415         var progressRun = function() {
18416             if (!mods.length) {
18417                 Roo.debug && Roo.log('hide?');
18418                 if (!this.hideProgress && Roo.MessageBox) {
18419                     Roo.MessageBox.hide();
18420                 }
18421                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18422                 
18423                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18424                 
18425                 // THE END...
18426                 return false;   
18427             }
18428             
18429             var m = mods.shift();
18430             
18431             
18432             Roo.debug && Roo.log(m);
18433             // not sure if this is supported any more.. - modules that are are just function
18434             if (typeof(m) == 'function') { 
18435                 m.call(this);
18436                 return progressRun.defer(10, _this);
18437             } 
18438             
18439             
18440             msg = "Building Interface " + (total  - mods.length) + 
18441                     " of " + total + 
18442                     (m.name ? (' - ' + m.name) : '');
18443                         Roo.debug && Roo.log(msg);
18444             if (!_this.hideProgress &&  Roo.MessageBox) { 
18445                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18446             }
18447             
18448          
18449             // is the module disabled?
18450             var disabled = (typeof(m.disabled) == 'function') ?
18451                 m.disabled.call(m.module.disabled) : m.disabled;    
18452             
18453             
18454             if (disabled) {
18455                 return progressRun(); // we do not update the display!
18456             }
18457             
18458             // now build 
18459             
18460                         
18461                         
18462             m.render();
18463             // it's 10 on top level, and 1 on others??? why...
18464             return progressRun.defer(10, _this);
18465              
18466         }
18467         progressRun.defer(1, _this);
18468      
18469         
18470         
18471     },
18472     /**
18473      * Overlay a set of modified strings onto a component
18474      * This is dependant on our builder exporting the strings and 'named strings' elements.
18475      * 
18476      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18477      * @param {Object} associative array of 'named' string and it's new value.
18478      * 
18479      */
18480         overlayStrings : function( component, strings )
18481     {
18482         if (typeof(component['_named_strings']) == 'undefined') {
18483             throw "ERROR: component does not have _named_strings";
18484         }
18485         for ( var k in strings ) {
18486             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18487             if (md !== false) {
18488                 component['_strings'][md] = strings[k];
18489             } else {
18490                 Roo.log('could not find named string: ' + k + ' in');
18491                 Roo.log(component);
18492             }
18493             
18494         }
18495         
18496     },
18497     
18498         
18499         /**
18500          * Event Object.
18501          *
18502          *
18503          */
18504         event: false, 
18505     /**
18506          * wrapper for event.on - aliased later..  
18507          * Typically use to register a event handler for register:
18508          *
18509          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18510          *
18511          */
18512     on : false
18513    
18514     
18515     
18516 });
18517
18518 Roo.XComponent.event = new Roo.util.Observable({
18519                 events : { 
18520                         /**
18521                          * @event register
18522                          * Fires when an Component is registered,
18523                          * set the disable property on the Component to stop registration.
18524                          * @param {Roo.XComponent} c the component being registerd.
18525                          * 
18526                          */
18527                         'register' : true,
18528             /**
18529                          * @event beforebuild
18530                          * Fires before each Component is built
18531                          * can be used to apply permissions.
18532                          * @param {Roo.XComponent} c the component being registerd.
18533                          * 
18534                          */
18535                         'beforebuild' : true,
18536                         /**
18537                          * @event buildcomplete
18538                          * Fires on the top level element when all elements have been built
18539                          * @param {Roo.XComponent} the top level component.
18540                          */
18541                         'buildcomplete' : true
18542                         
18543                 }
18544 });
18545
18546 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18547  //
18548  /**
18549  * marked - a markdown parser
18550  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18551  * https://github.com/chjj/marked
18552  */
18553
18554
18555 /**
18556  *
18557  * Roo.Markdown - is a very crude wrapper around marked..
18558  *
18559  * usage:
18560  * 
18561  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18562  * 
18563  * Note: move the sample code to the bottom of this
18564  * file before uncommenting it.
18565  *
18566  */
18567
18568 Roo.Markdown = {};
18569 Roo.Markdown.toHtml = function(text) {
18570     
18571     var c = new Roo.Markdown.marked.setOptions({
18572             renderer: new Roo.Markdown.marked.Renderer(),
18573             gfm: true,
18574             tables: true,
18575             breaks: false,
18576             pedantic: false,
18577             sanitize: false,
18578             smartLists: true,
18579             smartypants: false
18580           });
18581     // A FEW HACKS!!?
18582     
18583     text = text.replace(/\\\n/g,' ');
18584     return Roo.Markdown.marked(text);
18585 };
18586 //
18587 // converter
18588 //
18589 // Wraps all "globals" so that the only thing
18590 // exposed is makeHtml().
18591 //
18592 (function() {
18593     
18594      /**
18595          * eval:var:escape
18596          * eval:var:unescape
18597          * eval:var:replace
18598          */
18599       
18600     /**
18601      * Helpers
18602      */
18603     
18604     var escape = function (html, encode) {
18605       return html
18606         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18607         .replace(/</g, '&lt;')
18608         .replace(/>/g, '&gt;')
18609         .replace(/"/g, '&quot;')
18610         .replace(/'/g, '&#39;');
18611     }
18612     
18613     var unescape = function (html) {
18614         // explicitly match decimal, hex, and named HTML entities 
18615       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18616         n = n.toLowerCase();
18617         if (n === 'colon') { return ':'; }
18618         if (n.charAt(0) === '#') {
18619           return n.charAt(1) === 'x'
18620             ? String.fromCharCode(parseInt(n.substring(2), 16))
18621             : String.fromCharCode(+n.substring(1));
18622         }
18623         return '';
18624       });
18625     }
18626     
18627     var replace = function (regex, opt) {
18628       regex = regex.source;
18629       opt = opt || '';
18630       return function self(name, val) {
18631         if (!name) { return new RegExp(regex, opt); }
18632         val = val.source || val;
18633         val = val.replace(/(^|[^\[])\^/g, '$1');
18634         regex = regex.replace(name, val);
18635         return self;
18636       };
18637     }
18638
18639
18640          /**
18641          * eval:var:noop
18642     */
18643     var noop = function () {}
18644     noop.exec = noop;
18645     
18646          /**
18647          * eval:var:merge
18648     */
18649     var merge = function (obj) {
18650       var i = 1
18651         , target
18652         , key;
18653     
18654       for (; i < arguments.length; i++) {
18655         target = arguments[i];
18656         for (key in target) {
18657           if (Object.prototype.hasOwnProperty.call(target, key)) {
18658             obj[key] = target[key];
18659           }
18660         }
18661       }
18662     
18663       return obj;
18664     }
18665     
18666     
18667     /**
18668      * Block-Level Grammar
18669      */
18670     
18671     
18672     
18673     
18674     var block = {
18675       newline: /^\n+/,
18676       code: /^( {4}[^\n]+\n*)+/,
18677       fences: noop,
18678       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18679       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18680       nptable: noop,
18681       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18682       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18683       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18684       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18685       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18686       table: noop,
18687       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18688       text: /^[^\n]+/
18689     };
18690     
18691     block.bullet = /(?:[*+-]|\d+\.)/;
18692     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18693     block.item = replace(block.item, 'gm')
18694       (/bull/g, block.bullet)
18695       ();
18696     
18697     block.list = replace(block.list)
18698       (/bull/g, block.bullet)
18699       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18700       ('def', '\\n+(?=' + block.def.source + ')')
18701       ();
18702     
18703     block.blockquote = replace(block.blockquote)
18704       ('def', block.def)
18705       ();
18706     
18707     block._tag = '(?!(?:'
18708       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18709       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18710       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18711     
18712     block.html = replace(block.html)
18713       ('comment', /<!--[\s\S]*?-->/)
18714       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18715       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18716       (/tag/g, block._tag)
18717       ();
18718     
18719     block.paragraph = replace(block.paragraph)
18720       ('hr', block.hr)
18721       ('heading', block.heading)
18722       ('lheading', block.lheading)
18723       ('blockquote', block.blockquote)
18724       ('tag', '<' + block._tag)
18725       ('def', block.def)
18726       ();
18727     
18728     /**
18729      * Normal Block Grammar
18730      */
18731     
18732     block.normal = merge({}, block);
18733     
18734     /**
18735      * GFM Block Grammar
18736      */
18737     
18738     block.gfm = merge({}, block.normal, {
18739       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18740       paragraph: /^/,
18741       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18742     });
18743     
18744     block.gfm.paragraph = replace(block.paragraph)
18745       ('(?!', '(?!'
18746         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18747         + block.list.source.replace('\\1', '\\3') + '|')
18748       ();
18749     
18750     /**
18751      * GFM + Tables Block Grammar
18752      */
18753     
18754     block.tables = merge({}, block.gfm, {
18755       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18756       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18757     });
18758     
18759     /**
18760      * Block Lexer
18761      */
18762     
18763     var Lexer = function (options) {
18764       this.tokens = [];
18765       this.tokens.links = {};
18766       this.options = options || marked.defaults;
18767       this.rules = block.normal;
18768     
18769       if (this.options.gfm) {
18770         if (this.options.tables) {
18771           this.rules = block.tables;
18772         } else {
18773           this.rules = block.gfm;
18774         }
18775       }
18776     }
18777     
18778     /**
18779      * Expose Block Rules
18780      */
18781     
18782     Lexer.rules = block;
18783     
18784     /**
18785      * Static Lex Method
18786      */
18787     
18788     Lexer.lex = function(src, options) {
18789       var lexer = new Lexer(options);
18790       return lexer.lex(src);
18791     };
18792     
18793     /**
18794      * Preprocessing
18795      */
18796     
18797     Lexer.prototype.lex = function(src) {
18798       src = src
18799         .replace(/\r\n|\r/g, '\n')
18800         .replace(/\t/g, '    ')
18801         .replace(/\u00a0/g, ' ')
18802         .replace(/\u2424/g, '\n');
18803     
18804       return this.token(src, true);
18805     };
18806     
18807     /**
18808      * Lexing
18809      */
18810     
18811     Lexer.prototype.token = function(src, top, bq) {
18812       var src = src.replace(/^ +$/gm, '')
18813         , next
18814         , loose
18815         , cap
18816         , bull
18817         , b
18818         , item
18819         , space
18820         , i
18821         , l;
18822     
18823       while (src) {
18824         // newline
18825         if (cap = this.rules.newline.exec(src)) {
18826           src = src.substring(cap[0].length);
18827           if (cap[0].length > 1) {
18828             this.tokens.push({
18829               type: 'space'
18830             });
18831           }
18832         }
18833     
18834         // code
18835         if (cap = this.rules.code.exec(src)) {
18836           src = src.substring(cap[0].length);
18837           cap = cap[0].replace(/^ {4}/gm, '');
18838           this.tokens.push({
18839             type: 'code',
18840             text: !this.options.pedantic
18841               ? cap.replace(/\n+$/, '')
18842               : cap
18843           });
18844           continue;
18845         }
18846     
18847         // fences (gfm)
18848         if (cap = this.rules.fences.exec(src)) {
18849           src = src.substring(cap[0].length);
18850           this.tokens.push({
18851             type: 'code',
18852             lang: cap[2],
18853             text: cap[3] || ''
18854           });
18855           continue;
18856         }
18857     
18858         // heading
18859         if (cap = this.rules.heading.exec(src)) {
18860           src = src.substring(cap[0].length);
18861           this.tokens.push({
18862             type: 'heading',
18863             depth: cap[1].length,
18864             text: cap[2]
18865           });
18866           continue;
18867         }
18868     
18869         // table no leading pipe (gfm)
18870         if (top && (cap = this.rules.nptable.exec(src))) {
18871           src = src.substring(cap[0].length);
18872     
18873           item = {
18874             type: 'table',
18875             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18876             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18877             cells: cap[3].replace(/\n$/, '').split('\n')
18878           };
18879     
18880           for (i = 0; i < item.align.length; i++) {
18881             if (/^ *-+: *$/.test(item.align[i])) {
18882               item.align[i] = 'right';
18883             } else if (/^ *:-+: *$/.test(item.align[i])) {
18884               item.align[i] = 'center';
18885             } else if (/^ *:-+ *$/.test(item.align[i])) {
18886               item.align[i] = 'left';
18887             } else {
18888               item.align[i] = null;
18889             }
18890           }
18891     
18892           for (i = 0; i < item.cells.length; i++) {
18893             item.cells[i] = item.cells[i].split(/ *\| */);
18894           }
18895     
18896           this.tokens.push(item);
18897     
18898           continue;
18899         }
18900     
18901         // lheading
18902         if (cap = this.rules.lheading.exec(src)) {
18903           src = src.substring(cap[0].length);
18904           this.tokens.push({
18905             type: 'heading',
18906             depth: cap[2] === '=' ? 1 : 2,
18907             text: cap[1]
18908           });
18909           continue;
18910         }
18911     
18912         // hr
18913         if (cap = this.rules.hr.exec(src)) {
18914           src = src.substring(cap[0].length);
18915           this.tokens.push({
18916             type: 'hr'
18917           });
18918           continue;
18919         }
18920     
18921         // blockquote
18922         if (cap = this.rules.blockquote.exec(src)) {
18923           src = src.substring(cap[0].length);
18924     
18925           this.tokens.push({
18926             type: 'blockquote_start'
18927           });
18928     
18929           cap = cap[0].replace(/^ *> ?/gm, '');
18930     
18931           // Pass `top` to keep the current
18932           // "toplevel" state. This is exactly
18933           // how markdown.pl works.
18934           this.token(cap, top, true);
18935     
18936           this.tokens.push({
18937             type: 'blockquote_end'
18938           });
18939     
18940           continue;
18941         }
18942     
18943         // list
18944         if (cap = this.rules.list.exec(src)) {
18945           src = src.substring(cap[0].length);
18946           bull = cap[2];
18947     
18948           this.tokens.push({
18949             type: 'list_start',
18950             ordered: bull.length > 1
18951           });
18952     
18953           // Get each top-level item.
18954           cap = cap[0].match(this.rules.item);
18955     
18956           next = false;
18957           l = cap.length;
18958           i = 0;
18959     
18960           for (; i < l; i++) {
18961             item = cap[i];
18962     
18963             // Remove the list item's bullet
18964             // so it is seen as the next token.
18965             space = item.length;
18966             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18967     
18968             // Outdent whatever the
18969             // list item contains. Hacky.
18970             if (~item.indexOf('\n ')) {
18971               space -= item.length;
18972               item = !this.options.pedantic
18973                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18974                 : item.replace(/^ {1,4}/gm, '');
18975             }
18976     
18977             // Determine whether the next list item belongs here.
18978             // Backpedal if it does not belong in this list.
18979             if (this.options.smartLists && i !== l - 1) {
18980               b = block.bullet.exec(cap[i + 1])[0];
18981               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18982                 src = cap.slice(i + 1).join('\n') + src;
18983                 i = l - 1;
18984               }
18985             }
18986     
18987             // Determine whether item is loose or not.
18988             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18989             // for discount behavior.
18990             loose = next || /\n\n(?!\s*$)/.test(item);
18991             if (i !== l - 1) {
18992               next = item.charAt(item.length - 1) === '\n';
18993               if (!loose) { loose = next; }
18994             }
18995     
18996             this.tokens.push({
18997               type: loose
18998                 ? 'loose_item_start'
18999                 : 'list_item_start'
19000             });
19001     
19002             // Recurse.
19003             this.token(item, false, bq);
19004     
19005             this.tokens.push({
19006               type: 'list_item_end'
19007             });
19008           }
19009     
19010           this.tokens.push({
19011             type: 'list_end'
19012           });
19013     
19014           continue;
19015         }
19016     
19017         // html
19018         if (cap = this.rules.html.exec(src)) {
19019           src = src.substring(cap[0].length);
19020           this.tokens.push({
19021             type: this.options.sanitize
19022               ? 'paragraph'
19023               : 'html',
19024             pre: !this.options.sanitizer
19025               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19026             text: cap[0]
19027           });
19028           continue;
19029         }
19030     
19031         // def
19032         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19033           src = src.substring(cap[0].length);
19034           this.tokens.links[cap[1].toLowerCase()] = {
19035             href: cap[2],
19036             title: cap[3]
19037           };
19038           continue;
19039         }
19040     
19041         // table (gfm)
19042         if (top && (cap = this.rules.table.exec(src))) {
19043           src = src.substring(cap[0].length);
19044     
19045           item = {
19046             type: 'table',
19047             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19048             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19049             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19050           };
19051     
19052           for (i = 0; i < item.align.length; i++) {
19053             if (/^ *-+: *$/.test(item.align[i])) {
19054               item.align[i] = 'right';
19055             } else if (/^ *:-+: *$/.test(item.align[i])) {
19056               item.align[i] = 'center';
19057             } else if (/^ *:-+ *$/.test(item.align[i])) {
19058               item.align[i] = 'left';
19059             } else {
19060               item.align[i] = null;
19061             }
19062           }
19063     
19064           for (i = 0; i < item.cells.length; i++) {
19065             item.cells[i] = item.cells[i]
19066               .replace(/^ *\| *| *\| *$/g, '')
19067               .split(/ *\| */);
19068           }
19069     
19070           this.tokens.push(item);
19071     
19072           continue;
19073         }
19074     
19075         // top-level paragraph
19076         if (top && (cap = this.rules.paragraph.exec(src))) {
19077           src = src.substring(cap[0].length);
19078           this.tokens.push({
19079             type: 'paragraph',
19080             text: cap[1].charAt(cap[1].length - 1) === '\n'
19081               ? cap[1].slice(0, -1)
19082               : cap[1]
19083           });
19084           continue;
19085         }
19086     
19087         // text
19088         if (cap = this.rules.text.exec(src)) {
19089           // Top-level should never reach here.
19090           src = src.substring(cap[0].length);
19091           this.tokens.push({
19092             type: 'text',
19093             text: cap[0]
19094           });
19095           continue;
19096         }
19097     
19098         if (src) {
19099           throw new
19100             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19101         }
19102       }
19103     
19104       return this.tokens;
19105     };
19106     
19107     /**
19108      * Inline-Level Grammar
19109      */
19110     
19111     var inline = {
19112       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19113       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19114       url: noop,
19115       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19116       link: /^!?\[(inside)\]\(href\)/,
19117       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19118       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19119       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19120       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19121       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19122       br: /^ {2,}\n(?!\s*$)/,
19123       del: noop,
19124       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19125     };
19126     
19127     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19128     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19129     
19130     inline.link = replace(inline.link)
19131       ('inside', inline._inside)
19132       ('href', inline._href)
19133       ();
19134     
19135     inline.reflink = replace(inline.reflink)
19136       ('inside', inline._inside)
19137       ();
19138     
19139     /**
19140      * Normal Inline Grammar
19141      */
19142     
19143     inline.normal = merge({}, inline);
19144     
19145     /**
19146      * Pedantic Inline Grammar
19147      */
19148     
19149     inline.pedantic = merge({}, inline.normal, {
19150       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19151       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19152     });
19153     
19154     /**
19155      * GFM Inline Grammar
19156      */
19157     
19158     inline.gfm = merge({}, inline.normal, {
19159       escape: replace(inline.escape)('])', '~|])')(),
19160       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19161       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19162       text: replace(inline.text)
19163         (']|', '~]|')
19164         ('|', '|https?://|')
19165         ()
19166     });
19167     
19168     /**
19169      * GFM + Line Breaks Inline Grammar
19170      */
19171     
19172     inline.breaks = merge({}, inline.gfm, {
19173       br: replace(inline.br)('{2,}', '*')(),
19174       text: replace(inline.gfm.text)('{2,}', '*')()
19175     });
19176     
19177     /**
19178      * Inline Lexer & Compiler
19179      */
19180     
19181     var InlineLexer  = function (links, options) {
19182       this.options = options || marked.defaults;
19183       this.links = links;
19184       this.rules = inline.normal;
19185       this.renderer = this.options.renderer || new Renderer;
19186       this.renderer.options = this.options;
19187     
19188       if (!this.links) {
19189         throw new
19190           Error('Tokens array requires a `links` property.');
19191       }
19192     
19193       if (this.options.gfm) {
19194         if (this.options.breaks) {
19195           this.rules = inline.breaks;
19196         } else {
19197           this.rules = inline.gfm;
19198         }
19199       } else if (this.options.pedantic) {
19200         this.rules = inline.pedantic;
19201       }
19202     }
19203     
19204     /**
19205      * Expose Inline Rules
19206      */
19207     
19208     InlineLexer.rules = inline;
19209     
19210     /**
19211      * Static Lexing/Compiling Method
19212      */
19213     
19214     InlineLexer.output = function(src, links, options) {
19215       var inline = new InlineLexer(links, options);
19216       return inline.output(src);
19217     };
19218     
19219     /**
19220      * Lexing/Compiling
19221      */
19222     
19223     InlineLexer.prototype.output = function(src) {
19224       var out = ''
19225         , link
19226         , text
19227         , href
19228         , cap;
19229     
19230       while (src) {
19231         // escape
19232         if (cap = this.rules.escape.exec(src)) {
19233           src = src.substring(cap[0].length);
19234           out += cap[1];
19235           continue;
19236         }
19237     
19238         // autolink
19239         if (cap = this.rules.autolink.exec(src)) {
19240           src = src.substring(cap[0].length);
19241           if (cap[2] === '@') {
19242             text = cap[1].charAt(6) === ':'
19243               ? this.mangle(cap[1].substring(7))
19244               : this.mangle(cap[1]);
19245             href = this.mangle('mailto:') + text;
19246           } else {
19247             text = escape(cap[1]);
19248             href = text;
19249           }
19250           out += this.renderer.link(href, null, text);
19251           continue;
19252         }
19253     
19254         // url (gfm)
19255         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19256           src = src.substring(cap[0].length);
19257           text = escape(cap[1]);
19258           href = text;
19259           out += this.renderer.link(href, null, text);
19260           continue;
19261         }
19262     
19263         // tag
19264         if (cap = this.rules.tag.exec(src)) {
19265           if (!this.inLink && /^<a /i.test(cap[0])) {
19266             this.inLink = true;
19267           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19268             this.inLink = false;
19269           }
19270           src = src.substring(cap[0].length);
19271           out += this.options.sanitize
19272             ? this.options.sanitizer
19273               ? this.options.sanitizer(cap[0])
19274               : escape(cap[0])
19275             : cap[0];
19276           continue;
19277         }
19278     
19279         // link
19280         if (cap = this.rules.link.exec(src)) {
19281           src = src.substring(cap[0].length);
19282           this.inLink = true;
19283           out += this.outputLink(cap, {
19284             href: cap[2],
19285             title: cap[3]
19286           });
19287           this.inLink = false;
19288           continue;
19289         }
19290     
19291         // reflink, nolink
19292         if ((cap = this.rules.reflink.exec(src))
19293             || (cap = this.rules.nolink.exec(src))) {
19294           src = src.substring(cap[0].length);
19295           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19296           link = this.links[link.toLowerCase()];
19297           if (!link || !link.href) {
19298             out += cap[0].charAt(0);
19299             src = cap[0].substring(1) + src;
19300             continue;
19301           }
19302           this.inLink = true;
19303           out += this.outputLink(cap, link);
19304           this.inLink = false;
19305           continue;
19306         }
19307     
19308         // strong
19309         if (cap = this.rules.strong.exec(src)) {
19310           src = src.substring(cap[0].length);
19311           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19312           continue;
19313         }
19314     
19315         // em
19316         if (cap = this.rules.em.exec(src)) {
19317           src = src.substring(cap[0].length);
19318           out += this.renderer.em(this.output(cap[2] || cap[1]));
19319           continue;
19320         }
19321     
19322         // code
19323         if (cap = this.rules.code.exec(src)) {
19324           src = src.substring(cap[0].length);
19325           out += this.renderer.codespan(escape(cap[2], true));
19326           continue;
19327         }
19328     
19329         // br
19330         if (cap = this.rules.br.exec(src)) {
19331           src = src.substring(cap[0].length);
19332           out += this.renderer.br();
19333           continue;
19334         }
19335     
19336         // del (gfm)
19337         if (cap = this.rules.del.exec(src)) {
19338           src = src.substring(cap[0].length);
19339           out += this.renderer.del(this.output(cap[1]));
19340           continue;
19341         }
19342     
19343         // text
19344         if (cap = this.rules.text.exec(src)) {
19345           src = src.substring(cap[0].length);
19346           out += this.renderer.text(escape(this.smartypants(cap[0])));
19347           continue;
19348         }
19349     
19350         if (src) {
19351           throw new
19352             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19353         }
19354       }
19355     
19356       return out;
19357     };
19358     
19359     /**
19360      * Compile Link
19361      */
19362     
19363     InlineLexer.prototype.outputLink = function(cap, link) {
19364       var href = escape(link.href)
19365         , title = link.title ? escape(link.title) : null;
19366     
19367       return cap[0].charAt(0) !== '!'
19368         ? this.renderer.link(href, title, this.output(cap[1]))
19369         : this.renderer.image(href, title, escape(cap[1]));
19370     };
19371     
19372     /**
19373      * Smartypants Transformations
19374      */
19375     
19376     InlineLexer.prototype.smartypants = function(text) {
19377       if (!this.options.smartypants)  { return text; }
19378       return text
19379         // em-dashes
19380         .replace(/---/g, '\u2014')
19381         // en-dashes
19382         .replace(/--/g, '\u2013')
19383         // opening singles
19384         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19385         // closing singles & apostrophes
19386         .replace(/'/g, '\u2019')
19387         // opening doubles
19388         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19389         // closing doubles
19390         .replace(/"/g, '\u201d')
19391         // ellipses
19392         .replace(/\.{3}/g, '\u2026');
19393     };
19394     
19395     /**
19396      * Mangle Links
19397      */
19398     
19399     InlineLexer.prototype.mangle = function(text) {
19400       if (!this.options.mangle) { return text; }
19401       var out = ''
19402         , l = text.length
19403         , i = 0
19404         , ch;
19405     
19406       for (; i < l; i++) {
19407         ch = text.charCodeAt(i);
19408         if (Math.random() > 0.5) {
19409           ch = 'x' + ch.toString(16);
19410         }
19411         out += '&#' + ch + ';';
19412       }
19413     
19414       return out;
19415     };
19416     
19417     /**
19418      * Renderer
19419      */
19420     
19421      /**
19422          * eval:var:Renderer
19423     */
19424     
19425     var Renderer   = function (options) {
19426       this.options = options || {};
19427     }
19428     
19429     Renderer.prototype.code = function(code, lang, escaped) {
19430       if (this.options.highlight) {
19431         var out = this.options.highlight(code, lang);
19432         if (out != null && out !== code) {
19433           escaped = true;
19434           code = out;
19435         }
19436       } else {
19437             // hack!!! - it's already escapeD?
19438             escaped = true;
19439       }
19440     
19441       if (!lang) {
19442         return '<pre><code>'
19443           + (escaped ? code : escape(code, true))
19444           + '\n</code></pre>';
19445       }
19446     
19447       return '<pre><code class="'
19448         + this.options.langPrefix
19449         + escape(lang, true)
19450         + '">'
19451         + (escaped ? code : escape(code, true))
19452         + '\n</code></pre>\n';
19453     };
19454     
19455     Renderer.prototype.blockquote = function(quote) {
19456       return '<blockquote>\n' + quote + '</blockquote>\n';
19457     };
19458     
19459     Renderer.prototype.html = function(html) {
19460       return html;
19461     };
19462     
19463     Renderer.prototype.heading = function(text, level, raw) {
19464       return '<h'
19465         + level
19466         + ' id="'
19467         + this.options.headerPrefix
19468         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19469         + '">'
19470         + text
19471         + '</h'
19472         + level
19473         + '>\n';
19474     };
19475     
19476     Renderer.prototype.hr = function() {
19477       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19478     };
19479     
19480     Renderer.prototype.list = function(body, ordered) {
19481       var type = ordered ? 'ol' : 'ul';
19482       return '<' + type + '>\n' + body + '</' + type + '>\n';
19483     };
19484     
19485     Renderer.prototype.listitem = function(text) {
19486       return '<li>' + text + '</li>\n';
19487     };
19488     
19489     Renderer.prototype.paragraph = function(text) {
19490       return '<p>' + text + '</p>\n';
19491     };
19492     
19493     Renderer.prototype.table = function(header, body) {
19494       return '<table class="table table-striped">\n'
19495         + '<thead>\n'
19496         + header
19497         + '</thead>\n'
19498         + '<tbody>\n'
19499         + body
19500         + '</tbody>\n'
19501         + '</table>\n';
19502     };
19503     
19504     Renderer.prototype.tablerow = function(content) {
19505       return '<tr>\n' + content + '</tr>\n';
19506     };
19507     
19508     Renderer.prototype.tablecell = function(content, flags) {
19509       var type = flags.header ? 'th' : 'td';
19510       var tag = flags.align
19511         ? '<' + type + ' style="text-align:' + flags.align + '">'
19512         : '<' + type + '>';
19513       return tag + content + '</' + type + '>\n';
19514     };
19515     
19516     // span level renderer
19517     Renderer.prototype.strong = function(text) {
19518       return '<strong>' + text + '</strong>';
19519     };
19520     
19521     Renderer.prototype.em = function(text) {
19522       return '<em>' + text + '</em>';
19523     };
19524     
19525     Renderer.prototype.codespan = function(text) {
19526       return '<code>' + text + '</code>';
19527     };
19528     
19529     Renderer.prototype.br = function() {
19530       return this.options.xhtml ? '<br/>' : '<br>';
19531     };
19532     
19533     Renderer.prototype.del = function(text) {
19534       return '<del>' + text + '</del>';
19535     };
19536     
19537     Renderer.prototype.link = function(href, title, text) {
19538       if (this.options.sanitize) {
19539         try {
19540           var prot = decodeURIComponent(unescape(href))
19541             .replace(/[^\w:]/g, '')
19542             .toLowerCase();
19543         } catch (e) {
19544           return '';
19545         }
19546         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19547           return '';
19548         }
19549       }
19550       var out = '<a href="' + href + '"';
19551       if (title) {
19552         out += ' title="' + title + '"';
19553       }
19554       out += '>' + text + '</a>';
19555       return out;
19556     };
19557     
19558     Renderer.prototype.image = function(href, title, text) {
19559       var out = '<img src="' + href + '" alt="' + text + '"';
19560       if (title) {
19561         out += ' title="' + title + '"';
19562       }
19563       out += this.options.xhtml ? '/>' : '>';
19564       return out;
19565     };
19566     
19567     Renderer.prototype.text = function(text) {
19568       return text;
19569     };
19570     
19571     /**
19572      * Parsing & Compiling
19573      */
19574          /**
19575          * eval:var:Parser
19576     */
19577     
19578     var Parser= function (options) {
19579       this.tokens = [];
19580       this.token = null;
19581       this.options = options || marked.defaults;
19582       this.options.renderer = this.options.renderer || new Renderer;
19583       this.renderer = this.options.renderer;
19584       this.renderer.options = this.options;
19585     }
19586     
19587     /**
19588      * Static Parse Method
19589      */
19590     
19591     Parser.parse = function(src, options, renderer) {
19592       var parser = new Parser(options, renderer);
19593       return parser.parse(src);
19594     };
19595     
19596     /**
19597      * Parse Loop
19598      */
19599     
19600     Parser.prototype.parse = function(src) {
19601       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19602       this.tokens = src.reverse();
19603     
19604       var out = '';
19605       while (this.next()) {
19606         out += this.tok();
19607       }
19608     
19609       return out;
19610     };
19611     
19612     /**
19613      * Next Token
19614      */
19615     
19616     Parser.prototype.next = function() {
19617       return this.token = this.tokens.pop();
19618     };
19619     
19620     /**
19621      * Preview Next Token
19622      */
19623     
19624     Parser.prototype.peek = function() {
19625       return this.tokens[this.tokens.length - 1] || 0;
19626     };
19627     
19628     /**
19629      * Parse Text Tokens
19630      */
19631     
19632     Parser.prototype.parseText = function() {
19633       var body = this.token.text;
19634     
19635       while (this.peek().type === 'text') {
19636         body += '\n' + this.next().text;
19637       }
19638     
19639       return this.inline.output(body);
19640     };
19641     
19642     /**
19643      * Parse Current Token
19644      */
19645     
19646     Parser.prototype.tok = function() {
19647       switch (this.token.type) {
19648         case 'space': {
19649           return '';
19650         }
19651         case 'hr': {
19652           return this.renderer.hr();
19653         }
19654         case 'heading': {
19655           return this.renderer.heading(
19656             this.inline.output(this.token.text),
19657             this.token.depth,
19658             this.token.text);
19659         }
19660         case 'code': {
19661           return this.renderer.code(this.token.text,
19662             this.token.lang,
19663             this.token.escaped);
19664         }
19665         case 'table': {
19666           var header = ''
19667             , body = ''
19668             , i
19669             , row
19670             , cell
19671             , flags
19672             , j;
19673     
19674           // header
19675           cell = '';
19676           for (i = 0; i < this.token.header.length; i++) {
19677             flags = { header: true, align: this.token.align[i] };
19678             cell += this.renderer.tablecell(
19679               this.inline.output(this.token.header[i]),
19680               { header: true, align: this.token.align[i] }
19681             );
19682           }
19683           header += this.renderer.tablerow(cell);
19684     
19685           for (i = 0; i < this.token.cells.length; i++) {
19686             row = this.token.cells[i];
19687     
19688             cell = '';
19689             for (j = 0; j < row.length; j++) {
19690               cell += this.renderer.tablecell(
19691                 this.inline.output(row[j]),
19692                 { header: false, align: this.token.align[j] }
19693               );
19694             }
19695     
19696             body += this.renderer.tablerow(cell);
19697           }
19698           return this.renderer.table(header, body);
19699         }
19700         case 'blockquote_start': {
19701           var body = '';
19702     
19703           while (this.next().type !== 'blockquote_end') {
19704             body += this.tok();
19705           }
19706     
19707           return this.renderer.blockquote(body);
19708         }
19709         case 'list_start': {
19710           var body = ''
19711             , ordered = this.token.ordered;
19712     
19713           while (this.next().type !== 'list_end') {
19714             body += this.tok();
19715           }
19716     
19717           return this.renderer.list(body, ordered);
19718         }
19719         case 'list_item_start': {
19720           var body = '';
19721     
19722           while (this.next().type !== 'list_item_end') {
19723             body += this.token.type === 'text'
19724               ? this.parseText()
19725               : this.tok();
19726           }
19727     
19728           return this.renderer.listitem(body);
19729         }
19730         case 'loose_item_start': {
19731           var body = '';
19732     
19733           while (this.next().type !== 'list_item_end') {
19734             body += this.tok();
19735           }
19736     
19737           return this.renderer.listitem(body);
19738         }
19739         case 'html': {
19740           var html = !this.token.pre && !this.options.pedantic
19741             ? this.inline.output(this.token.text)
19742             : this.token.text;
19743           return this.renderer.html(html);
19744         }
19745         case 'paragraph': {
19746           return this.renderer.paragraph(this.inline.output(this.token.text));
19747         }
19748         case 'text': {
19749           return this.renderer.paragraph(this.parseText());
19750         }
19751       }
19752     };
19753   
19754     
19755     /**
19756      * Marked
19757      */
19758          /**
19759          * eval:var:marked
19760     */
19761     var marked = function (src, opt, callback) {
19762       if (callback || typeof opt === 'function') {
19763         if (!callback) {
19764           callback = opt;
19765           opt = null;
19766         }
19767     
19768         opt = merge({}, marked.defaults, opt || {});
19769     
19770         var highlight = opt.highlight
19771           , tokens
19772           , pending
19773           , i = 0;
19774     
19775         try {
19776           tokens = Lexer.lex(src, opt)
19777         } catch (e) {
19778           return callback(e);
19779         }
19780     
19781         pending = tokens.length;
19782          /**
19783          * eval:var:done
19784     */
19785         var done = function(err) {
19786           if (err) {
19787             opt.highlight = highlight;
19788             return callback(err);
19789           }
19790     
19791           var out;
19792     
19793           try {
19794             out = Parser.parse(tokens, opt);
19795           } catch (e) {
19796             err = e;
19797           }
19798     
19799           opt.highlight = highlight;
19800     
19801           return err
19802             ? callback(err)
19803             : callback(null, out);
19804         };
19805     
19806         if (!highlight || highlight.length < 3) {
19807           return done();
19808         }
19809     
19810         delete opt.highlight;
19811     
19812         if (!pending) { return done(); }
19813     
19814         for (; i < tokens.length; i++) {
19815           (function(token) {
19816             if (token.type !== 'code') {
19817               return --pending || done();
19818             }
19819             return highlight(token.text, token.lang, function(err, code) {
19820               if (err) { return done(err); }
19821               if (code == null || code === token.text) {
19822                 return --pending || done();
19823               }
19824               token.text = code;
19825               token.escaped = true;
19826               --pending || done();
19827             });
19828           })(tokens[i]);
19829         }
19830     
19831         return;
19832       }
19833       try {
19834         if (opt) { opt = merge({}, marked.defaults, opt); }
19835         return Parser.parse(Lexer.lex(src, opt), opt);
19836       } catch (e) {
19837         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19838         if ((opt || marked.defaults).silent) {
19839           return '<p>An error occured:</p><pre>'
19840             + escape(e.message + '', true)
19841             + '</pre>';
19842         }
19843         throw e;
19844       }
19845     }
19846     
19847     /**
19848      * Options
19849      */
19850     
19851     marked.options =
19852     marked.setOptions = function(opt) {
19853       merge(marked.defaults, opt);
19854       return marked;
19855     };
19856     
19857     marked.defaults = {
19858       gfm: true,
19859       tables: true,
19860       breaks: false,
19861       pedantic: false,
19862       sanitize: false,
19863       sanitizer: null,
19864       mangle: true,
19865       smartLists: false,
19866       silent: false,
19867       highlight: null,
19868       langPrefix: 'lang-',
19869       smartypants: false,
19870       headerPrefix: '',
19871       renderer: new Renderer,
19872       xhtml: false
19873     };
19874     
19875     /**
19876      * Expose
19877      */
19878     
19879     marked.Parser = Parser;
19880     marked.parser = Parser.parse;
19881     
19882     marked.Renderer = Renderer;
19883     
19884     marked.Lexer = Lexer;
19885     marked.lexer = Lexer.lex;
19886     
19887     marked.InlineLexer = InlineLexer;
19888     marked.inlineLexer = InlineLexer.output;
19889     
19890     marked.parse = marked;
19891     
19892     Roo.Markdown.marked = marked;
19893
19894 })();/*
19895  * Based on:
19896  * Ext JS Library 1.1.1
19897  * Copyright(c) 2006-2007, Ext JS, LLC.
19898  *
19899  * Originally Released Under LGPL - original licence link has changed is not relivant.
19900  *
19901  * Fork - LGPL
19902  * <script type="text/javascript">
19903  */
19904
19905
19906
19907 /*
19908  * These classes are derivatives of the similarly named classes in the YUI Library.
19909  * The original license:
19910  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19911  * Code licensed under the BSD License:
19912  * http://developer.yahoo.net/yui/license.txt
19913  */
19914
19915 (function() {
19916
19917 var Event=Roo.EventManager;
19918 var Dom=Roo.lib.Dom;
19919
19920 /**
19921  * @class Roo.dd.DragDrop
19922  * @extends Roo.util.Observable
19923  * Defines the interface and base operation of items that that can be
19924  * dragged or can be drop targets.  It was designed to be extended, overriding
19925  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19926  * Up to three html elements can be associated with a DragDrop instance:
19927  * <ul>
19928  * <li>linked element: the element that is passed into the constructor.
19929  * This is the element which defines the boundaries for interaction with
19930  * other DragDrop objects.</li>
19931  * <li>handle element(s): The drag operation only occurs if the element that
19932  * was clicked matches a handle element.  By default this is the linked
19933  * element, but there are times that you will want only a portion of the
19934  * linked element to initiate the drag operation, and the setHandleElId()
19935  * method provides a way to define this.</li>
19936  * <li>drag element: this represents the element that would be moved along
19937  * with the cursor during a drag operation.  By default, this is the linked
19938  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19939  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19940  * </li>
19941  * </ul>
19942  * This class should not be instantiated until the onload event to ensure that
19943  * the associated elements are available.
19944  * The following would define a DragDrop obj that would interact with any
19945  * other DragDrop obj in the "group1" group:
19946  * <pre>
19947  *  dd = new Roo.dd.DragDrop("div1", "group1");
19948  * </pre>
19949  * Since none of the event handlers have been implemented, nothing would
19950  * actually happen if you were to run the code above.  Normally you would
19951  * override this class or one of the default implementations, but you can
19952  * also override the methods you want on an instance of the class...
19953  * <pre>
19954  *  dd.onDragDrop = function(e, id) {
19955  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19956  *  }
19957  * </pre>
19958  * @constructor
19959  * @param {String} id of the element that is linked to this instance
19960  * @param {String} sGroup the group of related DragDrop objects
19961  * @param {object} config an object containing configurable attributes
19962  *                Valid properties for DragDrop:
19963  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19964  */
19965 Roo.dd.DragDrop = function(id, sGroup, config) {
19966     if (id) {
19967         this.init(id, sGroup, config);
19968     }
19969     
19970 };
19971
19972 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19973
19974     /**
19975      * The id of the element associated with this object.  This is what we
19976      * refer to as the "linked element" because the size and position of
19977      * this element is used to determine when the drag and drop objects have
19978      * interacted.
19979      * @property id
19980      * @type String
19981      */
19982     id: null,
19983
19984     /**
19985      * Configuration attributes passed into the constructor
19986      * @property config
19987      * @type object
19988      */
19989     config: null,
19990
19991     /**
19992      * The id of the element that will be dragged.  By default this is same
19993      * as the linked element , but could be changed to another element. Ex:
19994      * Roo.dd.DDProxy
19995      * @property dragElId
19996      * @type String
19997      * @private
19998      */
19999     dragElId: null,
20000
20001     /**
20002      * the id of the element that initiates the drag operation.  By default
20003      * this is the linked element, but could be changed to be a child of this
20004      * element.  This lets us do things like only starting the drag when the
20005      * header element within the linked html element is clicked.
20006      * @property handleElId
20007      * @type String
20008      * @private
20009      */
20010     handleElId: null,
20011
20012     /**
20013      * An associative array of HTML tags that will be ignored if clicked.
20014      * @property invalidHandleTypes
20015      * @type {string: string}
20016      */
20017     invalidHandleTypes: null,
20018
20019     /**
20020      * An associative array of ids for elements that will be ignored if clicked
20021      * @property invalidHandleIds
20022      * @type {string: string}
20023      */
20024     invalidHandleIds: null,
20025
20026     /**
20027      * An indexted array of css class names for elements that will be ignored
20028      * if clicked.
20029      * @property invalidHandleClasses
20030      * @type string[]
20031      */
20032     invalidHandleClasses: null,
20033
20034     /**
20035      * The linked element's absolute X position at the time the drag was
20036      * started
20037      * @property startPageX
20038      * @type int
20039      * @private
20040      */
20041     startPageX: 0,
20042
20043     /**
20044      * The linked element's absolute X position at the time the drag was
20045      * started
20046      * @property startPageY
20047      * @type int
20048      * @private
20049      */
20050     startPageY: 0,
20051
20052     /**
20053      * The group defines a logical collection of DragDrop objects that are
20054      * related.  Instances only get events when interacting with other
20055      * DragDrop object in the same group.  This lets us define multiple
20056      * groups using a single DragDrop subclass if we want.
20057      * @property groups
20058      * @type {string: string}
20059      */
20060     groups: null,
20061
20062     /**
20063      * Individual drag/drop instances can be locked.  This will prevent
20064      * onmousedown start drag.
20065      * @property locked
20066      * @type boolean
20067      * @private
20068      */
20069     locked: false,
20070
20071     /**
20072      * Lock this instance
20073      * @method lock
20074      */
20075     lock: function() { this.locked = true; },
20076
20077     /**
20078      * Unlock this instace
20079      * @method unlock
20080      */
20081     unlock: function() { this.locked = false; },
20082
20083     /**
20084      * By default, all insances can be a drop target.  This can be disabled by
20085      * setting isTarget to false.
20086      * @method isTarget
20087      * @type boolean
20088      */
20089     isTarget: true,
20090
20091     /**
20092      * The padding configured for this drag and drop object for calculating
20093      * the drop zone intersection with this object.
20094      * @method padding
20095      * @type int[]
20096      */
20097     padding: null,
20098
20099     /**
20100      * Cached reference to the linked element
20101      * @property _domRef
20102      * @private
20103      */
20104     _domRef: null,
20105
20106     /**
20107      * Internal typeof flag
20108      * @property __ygDragDrop
20109      * @private
20110      */
20111     __ygDragDrop: true,
20112
20113     /**
20114      * Set to true when horizontal contraints are applied
20115      * @property constrainX
20116      * @type boolean
20117      * @private
20118      */
20119     constrainX: false,
20120
20121     /**
20122      * Set to true when vertical contraints are applied
20123      * @property constrainY
20124      * @type boolean
20125      * @private
20126      */
20127     constrainY: false,
20128
20129     /**
20130      * The left constraint
20131      * @property minX
20132      * @type int
20133      * @private
20134      */
20135     minX: 0,
20136
20137     /**
20138      * The right constraint
20139      * @property maxX
20140      * @type int
20141      * @private
20142      */
20143     maxX: 0,
20144
20145     /**
20146      * The up constraint
20147      * @property minY
20148      * @type int
20149      * @type int
20150      * @private
20151      */
20152     minY: 0,
20153
20154     /**
20155      * The down constraint
20156      * @property maxY
20157      * @type int
20158      * @private
20159      */
20160     maxY: 0,
20161
20162     /**
20163      * Maintain offsets when we resetconstraints.  Set to true when you want
20164      * the position of the element relative to its parent to stay the same
20165      * when the page changes
20166      *
20167      * @property maintainOffset
20168      * @type boolean
20169      */
20170     maintainOffset: false,
20171
20172     /**
20173      * Array of pixel locations the element will snap to if we specified a
20174      * horizontal graduation/interval.  This array is generated automatically
20175      * when you define a tick interval.
20176      * @property xTicks
20177      * @type int[]
20178      */
20179     xTicks: null,
20180
20181     /**
20182      * Array of pixel locations the element will snap to if we specified a
20183      * vertical graduation/interval.  This array is generated automatically
20184      * when you define a tick interval.
20185      * @property yTicks
20186      * @type int[]
20187      */
20188     yTicks: null,
20189
20190     /**
20191      * By default the drag and drop instance will only respond to the primary
20192      * button click (left button for a right-handed mouse).  Set to true to
20193      * allow drag and drop to start with any mouse click that is propogated
20194      * by the browser
20195      * @property primaryButtonOnly
20196      * @type boolean
20197      */
20198     primaryButtonOnly: true,
20199
20200     /**
20201      * The availabe property is false until the linked dom element is accessible.
20202      * @property available
20203      * @type boolean
20204      */
20205     available: false,
20206
20207     /**
20208      * By default, drags can only be initiated if the mousedown occurs in the
20209      * region the linked element is.  This is done in part to work around a
20210      * bug in some browsers that mis-report the mousedown if the previous
20211      * mouseup happened outside of the window.  This property is set to true
20212      * if outer handles are defined.
20213      *
20214      * @property hasOuterHandles
20215      * @type boolean
20216      * @default false
20217      */
20218     hasOuterHandles: false,
20219
20220     /**
20221      * Code that executes immediately before the startDrag event
20222      * @method b4StartDrag
20223      * @private
20224      */
20225     b4StartDrag: function(x, y) { },
20226
20227     /**
20228      * Abstract method called after a drag/drop object is clicked
20229      * and the drag or mousedown time thresholds have beeen met.
20230      * @method startDrag
20231      * @param {int} X click location
20232      * @param {int} Y click location
20233      */
20234     startDrag: function(x, y) { /* override this */ },
20235
20236     /**
20237      * Code that executes immediately before the onDrag event
20238      * @method b4Drag
20239      * @private
20240      */
20241     b4Drag: function(e) { },
20242
20243     /**
20244      * Abstract method called during the onMouseMove event while dragging an
20245      * object.
20246      * @method onDrag
20247      * @param {Event} e the mousemove event
20248      */
20249     onDrag: function(e) { /* override this */ },
20250
20251     /**
20252      * Abstract method called when this element fist begins hovering over
20253      * another DragDrop obj
20254      * @method onDragEnter
20255      * @param {Event} e the mousemove event
20256      * @param {String|DragDrop[]} id In POINT mode, the element
20257      * id this is hovering over.  In INTERSECT mode, an array of one or more
20258      * dragdrop items being hovered over.
20259      */
20260     onDragEnter: function(e, id) { /* override this */ },
20261
20262     /**
20263      * Code that executes immediately before the onDragOver event
20264      * @method b4DragOver
20265      * @private
20266      */
20267     b4DragOver: function(e) { },
20268
20269     /**
20270      * Abstract method called when this element is hovering over another
20271      * DragDrop obj
20272      * @method onDragOver
20273      * @param {Event} e the mousemove event
20274      * @param {String|DragDrop[]} id In POINT mode, the element
20275      * id this is hovering over.  In INTERSECT mode, an array of dd items
20276      * being hovered over.
20277      */
20278     onDragOver: function(e, id) { /* override this */ },
20279
20280     /**
20281      * Code that executes immediately before the onDragOut event
20282      * @method b4DragOut
20283      * @private
20284      */
20285     b4DragOut: function(e) { },
20286
20287     /**
20288      * Abstract method called when we are no longer hovering over an element
20289      * @method onDragOut
20290      * @param {Event} e the mousemove event
20291      * @param {String|DragDrop[]} id In POINT mode, the element
20292      * id this was hovering over.  In INTERSECT mode, an array of dd items
20293      * that the mouse is no longer over.
20294      */
20295     onDragOut: function(e, id) { /* override this */ },
20296
20297     /**
20298      * Code that executes immediately before the onDragDrop event
20299      * @method b4DragDrop
20300      * @private
20301      */
20302     b4DragDrop: function(e) { },
20303
20304     /**
20305      * Abstract method called when this item is dropped on another DragDrop
20306      * obj
20307      * @method onDragDrop
20308      * @param {Event} e the mouseup event
20309      * @param {String|DragDrop[]} id In POINT mode, the element
20310      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20311      * was dropped on.
20312      */
20313     onDragDrop: function(e, id) { /* override this */ },
20314
20315     /**
20316      * Abstract method called when this item is dropped on an area with no
20317      * drop target
20318      * @method onInvalidDrop
20319      * @param {Event} e the mouseup event
20320      */
20321     onInvalidDrop: function(e) { /* override this */ },
20322
20323     /**
20324      * Code that executes immediately before the endDrag event
20325      * @method b4EndDrag
20326      * @private
20327      */
20328     b4EndDrag: function(e) { },
20329
20330     /**
20331      * Fired when we are done dragging the object
20332      * @method endDrag
20333      * @param {Event} e the mouseup event
20334      */
20335     endDrag: function(e) { /* override this */ },
20336
20337     /**
20338      * Code executed immediately before the onMouseDown event
20339      * @method b4MouseDown
20340      * @param {Event} e the mousedown event
20341      * @private
20342      */
20343     b4MouseDown: function(e) {  },
20344
20345     /**
20346      * Event handler that fires when a drag/drop obj gets a mousedown
20347      * @method onMouseDown
20348      * @param {Event} e the mousedown event
20349      */
20350     onMouseDown: function(e) { /* override this */ },
20351
20352     /**
20353      * Event handler that fires when a drag/drop obj gets a mouseup
20354      * @method onMouseUp
20355      * @param {Event} e the mouseup event
20356      */
20357     onMouseUp: function(e) { /* override this */ },
20358
20359     /**
20360      * Override the onAvailable method to do what is needed after the initial
20361      * position was determined.
20362      * @method onAvailable
20363      */
20364     onAvailable: function () {
20365     },
20366
20367     /*
20368      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20369      * @type Object
20370      */
20371     defaultPadding : {left:0, right:0, top:0, bottom:0},
20372
20373     /*
20374      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20375  *
20376  * Usage:
20377  <pre><code>
20378  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20379                 { dragElId: "existingProxyDiv" });
20380  dd.startDrag = function(){
20381      this.constrainTo("parent-id");
20382  };
20383  </code></pre>
20384  * Or you can initalize it using the {@link Roo.Element} object:
20385  <pre><code>
20386  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20387      startDrag : function(){
20388          this.constrainTo("parent-id");
20389      }
20390  });
20391  </code></pre>
20392      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20393      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20394      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20395      * an object containing the sides to pad. For example: {right:10, bottom:10}
20396      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20397      */
20398     constrainTo : function(constrainTo, pad, inContent){
20399         if(typeof pad == "number"){
20400             pad = {left: pad, right:pad, top:pad, bottom:pad};
20401         }
20402         pad = pad || this.defaultPadding;
20403         var b = Roo.get(this.getEl()).getBox();
20404         var ce = Roo.get(constrainTo);
20405         var s = ce.getScroll();
20406         var c, cd = ce.dom;
20407         if(cd == document.body){
20408             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20409         }else{
20410             xy = ce.getXY();
20411             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20412         }
20413
20414
20415         var topSpace = b.y - c.y;
20416         var leftSpace = b.x - c.x;
20417
20418         this.resetConstraints();
20419         this.setXConstraint(leftSpace - (pad.left||0), // left
20420                 c.width - leftSpace - b.width - (pad.right||0) //right
20421         );
20422         this.setYConstraint(topSpace - (pad.top||0), //top
20423                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20424         );
20425     },
20426
20427     /**
20428      * Returns a reference to the linked element
20429      * @method getEl
20430      * @return {HTMLElement} the html element
20431      */
20432     getEl: function() {
20433         if (!this._domRef) {
20434             this._domRef = Roo.getDom(this.id);
20435         }
20436
20437         return this._domRef;
20438     },
20439
20440     /**
20441      * Returns a reference to the actual element to drag.  By default this is
20442      * the same as the html element, but it can be assigned to another
20443      * element. An example of this can be found in Roo.dd.DDProxy
20444      * @method getDragEl
20445      * @return {HTMLElement} the html element
20446      */
20447     getDragEl: function() {
20448         return Roo.getDom(this.dragElId);
20449     },
20450
20451     /**
20452      * Sets up the DragDrop object.  Must be called in the constructor of any
20453      * Roo.dd.DragDrop subclass
20454      * @method init
20455      * @param id the id of the linked element
20456      * @param {String} sGroup the group of related items
20457      * @param {object} config configuration attributes
20458      */
20459     init: function(id, sGroup, config) {
20460         this.initTarget(id, sGroup, config);
20461         if (!Roo.isTouch) {
20462             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20463         }
20464         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20465         // Event.on(this.id, "selectstart", Event.preventDefault);
20466     },
20467
20468     /**
20469      * Initializes Targeting functionality only... the object does not
20470      * get a mousedown handler.
20471      * @method initTarget
20472      * @param id the id of the linked element
20473      * @param {String} sGroup the group of related items
20474      * @param {object} config configuration attributes
20475      */
20476     initTarget: function(id, sGroup, config) {
20477
20478         // configuration attributes
20479         this.config = config || {};
20480
20481         // create a local reference to the drag and drop manager
20482         this.DDM = Roo.dd.DDM;
20483         // initialize the groups array
20484         this.groups = {};
20485
20486         // assume that we have an element reference instead of an id if the
20487         // parameter is not a string
20488         if (typeof id !== "string") {
20489             id = Roo.id(id);
20490         }
20491
20492         // set the id
20493         this.id = id;
20494
20495         // add to an interaction group
20496         this.addToGroup((sGroup) ? sGroup : "default");
20497
20498         // We don't want to register this as the handle with the manager
20499         // so we just set the id rather than calling the setter.
20500         this.handleElId = id;
20501
20502         // the linked element is the element that gets dragged by default
20503         this.setDragElId(id);
20504
20505         // by default, clicked anchors will not start drag operations.
20506         this.invalidHandleTypes = { A: "A" };
20507         this.invalidHandleIds = {};
20508         this.invalidHandleClasses = [];
20509
20510         this.applyConfig();
20511
20512         this.handleOnAvailable();
20513     },
20514
20515     /**
20516      * Applies the configuration parameters that were passed into the constructor.
20517      * This is supposed to happen at each level through the inheritance chain.  So
20518      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20519      * DragDrop in order to get all of the parameters that are available in
20520      * each object.
20521      * @method applyConfig
20522      */
20523     applyConfig: function() {
20524
20525         // configurable properties:
20526         //    padding, isTarget, maintainOffset, primaryButtonOnly
20527         this.padding           = this.config.padding || [0, 0, 0, 0];
20528         this.isTarget          = (this.config.isTarget !== false);
20529         this.maintainOffset    = (this.config.maintainOffset);
20530         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20531
20532     },
20533
20534     /**
20535      * Executed when the linked element is available
20536      * @method handleOnAvailable
20537      * @private
20538      */
20539     handleOnAvailable: function() {
20540         this.available = true;
20541         this.resetConstraints();
20542         this.onAvailable();
20543     },
20544
20545      /**
20546      * Configures the padding for the target zone in px.  Effectively expands
20547      * (or reduces) the virtual object size for targeting calculations.
20548      * Supports css-style shorthand; if only one parameter is passed, all sides
20549      * will have that padding, and if only two are passed, the top and bottom
20550      * will have the first param, the left and right the second.
20551      * @method setPadding
20552      * @param {int} iTop    Top pad
20553      * @param {int} iRight  Right pad
20554      * @param {int} iBot    Bot pad
20555      * @param {int} iLeft   Left pad
20556      */
20557     setPadding: function(iTop, iRight, iBot, iLeft) {
20558         // this.padding = [iLeft, iRight, iTop, iBot];
20559         if (!iRight && 0 !== iRight) {
20560             this.padding = [iTop, iTop, iTop, iTop];
20561         } else if (!iBot && 0 !== iBot) {
20562             this.padding = [iTop, iRight, iTop, iRight];
20563         } else {
20564             this.padding = [iTop, iRight, iBot, iLeft];
20565         }
20566     },
20567
20568     /**
20569      * Stores the initial placement of the linked element.
20570      * @method setInitialPosition
20571      * @param {int} diffX   the X offset, default 0
20572      * @param {int} diffY   the Y offset, default 0
20573      */
20574     setInitPosition: function(diffX, diffY) {
20575         var el = this.getEl();
20576
20577         if (!this.DDM.verifyEl(el)) {
20578             return;
20579         }
20580
20581         var dx = diffX || 0;
20582         var dy = diffY || 0;
20583
20584         var p = Dom.getXY( el );
20585
20586         this.initPageX = p[0] - dx;
20587         this.initPageY = p[1] - dy;
20588
20589         this.lastPageX = p[0];
20590         this.lastPageY = p[1];
20591
20592
20593         this.setStartPosition(p);
20594     },
20595
20596     /**
20597      * Sets the start position of the element.  This is set when the obj
20598      * is initialized, the reset when a drag is started.
20599      * @method setStartPosition
20600      * @param pos current position (from previous lookup)
20601      * @private
20602      */
20603     setStartPosition: function(pos) {
20604         var p = pos || Dom.getXY( this.getEl() );
20605         this.deltaSetXY = null;
20606
20607         this.startPageX = p[0];
20608         this.startPageY = p[1];
20609     },
20610
20611     /**
20612      * Add this instance to a group of related drag/drop objects.  All
20613      * instances belong to at least one group, and can belong to as many
20614      * groups as needed.
20615      * @method addToGroup
20616      * @param sGroup {string} the name of the group
20617      */
20618     addToGroup: function(sGroup) {
20619         this.groups[sGroup] = true;
20620         this.DDM.regDragDrop(this, sGroup);
20621     },
20622
20623     /**
20624      * Remove's this instance from the supplied interaction group
20625      * @method removeFromGroup
20626      * @param {string}  sGroup  The group to drop
20627      */
20628     removeFromGroup: function(sGroup) {
20629         if (this.groups[sGroup]) {
20630             delete this.groups[sGroup];
20631         }
20632
20633         this.DDM.removeDDFromGroup(this, sGroup);
20634     },
20635
20636     /**
20637      * Allows you to specify that an element other than the linked element
20638      * will be moved with the cursor during a drag
20639      * @method setDragElId
20640      * @param id {string} the id of the element that will be used to initiate the drag
20641      */
20642     setDragElId: function(id) {
20643         this.dragElId = id;
20644     },
20645
20646     /**
20647      * Allows you to specify a child of the linked element that should be
20648      * used to initiate the drag operation.  An example of this would be if
20649      * you have a content div with text and links.  Clicking anywhere in the
20650      * content area would normally start the drag operation.  Use this method
20651      * to specify that an element inside of the content div is the element
20652      * that starts the drag operation.
20653      * @method setHandleElId
20654      * @param id {string} the id of the element that will be used to
20655      * initiate the drag.
20656      */
20657     setHandleElId: function(id) {
20658         if (typeof id !== "string") {
20659             id = Roo.id(id);
20660         }
20661         this.handleElId = id;
20662         this.DDM.regHandle(this.id, id);
20663     },
20664
20665     /**
20666      * Allows you to set an element outside of the linked element as a drag
20667      * handle
20668      * @method setOuterHandleElId
20669      * @param id the id of the element that will be used to initiate the drag
20670      */
20671     setOuterHandleElId: function(id) {
20672         if (typeof id !== "string") {
20673             id = Roo.id(id);
20674         }
20675         Event.on(id, "mousedown",
20676                 this.handleMouseDown, this);
20677         this.setHandleElId(id);
20678
20679         this.hasOuterHandles = true;
20680     },
20681
20682     /**
20683      * Remove all drag and drop hooks for this element
20684      * @method unreg
20685      */
20686     unreg: function() {
20687         Event.un(this.id, "mousedown",
20688                 this.handleMouseDown);
20689         Event.un(this.id, "touchstart",
20690                 this.handleMouseDown);
20691         this._domRef = null;
20692         this.DDM._remove(this);
20693     },
20694
20695     destroy : function(){
20696         this.unreg();
20697     },
20698
20699     /**
20700      * Returns true if this instance is locked, or the drag drop mgr is locked
20701      * (meaning that all drag/drop is disabled on the page.)
20702      * @method isLocked
20703      * @return {boolean} true if this obj or all drag/drop is locked, else
20704      * false
20705      */
20706     isLocked: function() {
20707         return (this.DDM.isLocked() || this.locked);
20708     },
20709
20710     /**
20711      * Fired when this object is clicked
20712      * @method handleMouseDown
20713      * @param {Event} e
20714      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20715      * @private
20716      */
20717     handleMouseDown: function(e, oDD){
20718      
20719         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20720             //Roo.log('not touch/ button !=0');
20721             return;
20722         }
20723         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20724             return; // double touch..
20725         }
20726         
20727
20728         if (this.isLocked()) {
20729             //Roo.log('locked');
20730             return;
20731         }
20732
20733         this.DDM.refreshCache(this.groups);
20734 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20735         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20736         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20737             //Roo.log('no outer handes or not over target');
20738                 // do nothing.
20739         } else {
20740 //            Roo.log('check validator');
20741             if (this.clickValidator(e)) {
20742 //                Roo.log('validate success');
20743                 // set the initial element position
20744                 this.setStartPosition();
20745
20746
20747                 this.b4MouseDown(e);
20748                 this.onMouseDown(e);
20749
20750                 this.DDM.handleMouseDown(e, this);
20751
20752                 this.DDM.stopEvent(e);
20753             } else {
20754
20755
20756             }
20757         }
20758     },
20759
20760     clickValidator: function(e) {
20761         var target = e.getTarget();
20762         return ( this.isValidHandleChild(target) &&
20763                     (this.id == this.handleElId ||
20764                         this.DDM.handleWasClicked(target, this.id)) );
20765     },
20766
20767     /**
20768      * Allows you to specify a tag name that should not start a drag operation
20769      * when clicked.  This is designed to facilitate embedding links within a
20770      * drag handle that do something other than start the drag.
20771      * @method addInvalidHandleType
20772      * @param {string} tagName the type of element to exclude
20773      */
20774     addInvalidHandleType: function(tagName) {
20775         var type = tagName.toUpperCase();
20776         this.invalidHandleTypes[type] = type;
20777     },
20778
20779     /**
20780      * Lets you to specify an element id for a child of a drag handle
20781      * that should not initiate a drag
20782      * @method addInvalidHandleId
20783      * @param {string} id the element id of the element you wish to ignore
20784      */
20785     addInvalidHandleId: function(id) {
20786         if (typeof id !== "string") {
20787             id = Roo.id(id);
20788         }
20789         this.invalidHandleIds[id] = id;
20790     },
20791
20792     /**
20793      * Lets you specify a css class of elements that will not initiate a drag
20794      * @method addInvalidHandleClass
20795      * @param {string} cssClass the class of the elements you wish to ignore
20796      */
20797     addInvalidHandleClass: function(cssClass) {
20798         this.invalidHandleClasses.push(cssClass);
20799     },
20800
20801     /**
20802      * Unsets an excluded tag name set by addInvalidHandleType
20803      * @method removeInvalidHandleType
20804      * @param {string} tagName the type of element to unexclude
20805      */
20806     removeInvalidHandleType: function(tagName) {
20807         var type = tagName.toUpperCase();
20808         // this.invalidHandleTypes[type] = null;
20809         delete this.invalidHandleTypes[type];
20810     },
20811
20812     /**
20813      * Unsets an invalid handle id
20814      * @method removeInvalidHandleId
20815      * @param {string} id the id of the element to re-enable
20816      */
20817     removeInvalidHandleId: function(id) {
20818         if (typeof id !== "string") {
20819             id = Roo.id(id);
20820         }
20821         delete this.invalidHandleIds[id];
20822     },
20823
20824     /**
20825      * Unsets an invalid css class
20826      * @method removeInvalidHandleClass
20827      * @param {string} cssClass the class of the element(s) you wish to
20828      * re-enable
20829      */
20830     removeInvalidHandleClass: function(cssClass) {
20831         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20832             if (this.invalidHandleClasses[i] == cssClass) {
20833                 delete this.invalidHandleClasses[i];
20834             }
20835         }
20836     },
20837
20838     /**
20839      * Checks the tag exclusion list to see if this click should be ignored
20840      * @method isValidHandleChild
20841      * @param {HTMLElement} node the HTMLElement to evaluate
20842      * @return {boolean} true if this is a valid tag type, false if not
20843      */
20844     isValidHandleChild: function(node) {
20845
20846         var valid = true;
20847         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20848         var nodeName;
20849         try {
20850             nodeName = node.nodeName.toUpperCase();
20851         } catch(e) {
20852             nodeName = node.nodeName;
20853         }
20854         valid = valid && !this.invalidHandleTypes[nodeName];
20855         valid = valid && !this.invalidHandleIds[node.id];
20856
20857         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20858             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20859         }
20860
20861
20862         return valid;
20863
20864     },
20865
20866     /**
20867      * Create the array of horizontal tick marks if an interval was specified
20868      * in setXConstraint().
20869      * @method setXTicks
20870      * @private
20871      */
20872     setXTicks: function(iStartX, iTickSize) {
20873         this.xTicks = [];
20874         this.xTickSize = iTickSize;
20875
20876         var tickMap = {};
20877
20878         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20879             if (!tickMap[i]) {
20880                 this.xTicks[this.xTicks.length] = i;
20881                 tickMap[i] = true;
20882             }
20883         }
20884
20885         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20886             if (!tickMap[i]) {
20887                 this.xTicks[this.xTicks.length] = i;
20888                 tickMap[i] = true;
20889             }
20890         }
20891
20892         this.xTicks.sort(this.DDM.numericSort) ;
20893     },
20894
20895     /**
20896      * Create the array of vertical tick marks if an interval was specified in
20897      * setYConstraint().
20898      * @method setYTicks
20899      * @private
20900      */
20901     setYTicks: function(iStartY, iTickSize) {
20902         this.yTicks = [];
20903         this.yTickSize = iTickSize;
20904
20905         var tickMap = {};
20906
20907         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20908             if (!tickMap[i]) {
20909                 this.yTicks[this.yTicks.length] = i;
20910                 tickMap[i] = true;
20911             }
20912         }
20913
20914         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20915             if (!tickMap[i]) {
20916                 this.yTicks[this.yTicks.length] = i;
20917                 tickMap[i] = true;
20918             }
20919         }
20920
20921         this.yTicks.sort(this.DDM.numericSort) ;
20922     },
20923
20924     /**
20925      * By default, the element can be dragged any place on the screen.  Use
20926      * this method to limit the horizontal travel of the element.  Pass in
20927      * 0,0 for the parameters if you want to lock the drag to the y axis.
20928      * @method setXConstraint
20929      * @param {int} iLeft the number of pixels the element can move to the left
20930      * @param {int} iRight the number of pixels the element can move to the
20931      * right
20932      * @param {int} iTickSize optional parameter for specifying that the
20933      * element
20934      * should move iTickSize pixels at a time.
20935      */
20936     setXConstraint: function(iLeft, iRight, iTickSize) {
20937         this.leftConstraint = iLeft;
20938         this.rightConstraint = iRight;
20939
20940         this.minX = this.initPageX - iLeft;
20941         this.maxX = this.initPageX + iRight;
20942         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20943
20944         this.constrainX = true;
20945     },
20946
20947     /**
20948      * Clears any constraints applied to this instance.  Also clears ticks
20949      * since they can't exist independent of a constraint at this time.
20950      * @method clearConstraints
20951      */
20952     clearConstraints: function() {
20953         this.constrainX = false;
20954         this.constrainY = false;
20955         this.clearTicks();
20956     },
20957
20958     /**
20959      * Clears any tick interval defined for this instance
20960      * @method clearTicks
20961      */
20962     clearTicks: function() {
20963         this.xTicks = null;
20964         this.yTicks = null;
20965         this.xTickSize = 0;
20966         this.yTickSize = 0;
20967     },
20968
20969     /**
20970      * By default, the element can be dragged any place on the screen.  Set
20971      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20972      * parameters if you want to lock the drag to the x axis.
20973      * @method setYConstraint
20974      * @param {int} iUp the number of pixels the element can move up
20975      * @param {int} iDown the number of pixels the element can move down
20976      * @param {int} iTickSize optional parameter for specifying that the
20977      * element should move iTickSize pixels at a time.
20978      */
20979     setYConstraint: function(iUp, iDown, iTickSize) {
20980         this.topConstraint = iUp;
20981         this.bottomConstraint = iDown;
20982
20983         this.minY = this.initPageY - iUp;
20984         this.maxY = this.initPageY + iDown;
20985         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20986
20987         this.constrainY = true;
20988
20989     },
20990
20991     /**
20992      * resetConstraints must be called if you manually reposition a dd element.
20993      * @method resetConstraints
20994      * @param {boolean} maintainOffset
20995      */
20996     resetConstraints: function() {
20997
20998
20999         // Maintain offsets if necessary
21000         if (this.initPageX || this.initPageX === 0) {
21001             // figure out how much this thing has moved
21002             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21003             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21004
21005             this.setInitPosition(dx, dy);
21006
21007         // This is the first time we have detected the element's position
21008         } else {
21009             this.setInitPosition();
21010         }
21011
21012         if (this.constrainX) {
21013             this.setXConstraint( this.leftConstraint,
21014                                  this.rightConstraint,
21015                                  this.xTickSize        );
21016         }
21017
21018         if (this.constrainY) {
21019             this.setYConstraint( this.topConstraint,
21020                                  this.bottomConstraint,
21021                                  this.yTickSize         );
21022         }
21023     },
21024
21025     /**
21026      * Normally the drag element is moved pixel by pixel, but we can specify
21027      * that it move a number of pixels at a time.  This method resolves the
21028      * location when we have it set up like this.
21029      * @method getTick
21030      * @param {int} val where we want to place the object
21031      * @param {int[]} tickArray sorted array of valid points
21032      * @return {int} the closest tick
21033      * @private
21034      */
21035     getTick: function(val, tickArray) {
21036
21037         if (!tickArray) {
21038             // If tick interval is not defined, it is effectively 1 pixel,
21039             // so we return the value passed to us.
21040             return val;
21041         } else if (tickArray[0] >= val) {
21042             // The value is lower than the first tick, so we return the first
21043             // tick.
21044             return tickArray[0];
21045         } else {
21046             for (var i=0, len=tickArray.length; i<len; ++i) {
21047                 var next = i + 1;
21048                 if (tickArray[next] && tickArray[next] >= val) {
21049                     var diff1 = val - tickArray[i];
21050                     var diff2 = tickArray[next] - val;
21051                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21052                 }
21053             }
21054
21055             // The value is larger than the last tick, so we return the last
21056             // tick.
21057             return tickArray[tickArray.length - 1];
21058         }
21059     },
21060
21061     /**
21062      * toString method
21063      * @method toString
21064      * @return {string} string representation of the dd obj
21065      */
21066     toString: function() {
21067         return ("DragDrop " + this.id);
21068     }
21069
21070 });
21071
21072 })();
21073 /*
21074  * Based on:
21075  * Ext JS Library 1.1.1
21076  * Copyright(c) 2006-2007, Ext JS, LLC.
21077  *
21078  * Originally Released Under LGPL - original licence link has changed is not relivant.
21079  *
21080  * Fork - LGPL
21081  * <script type="text/javascript">
21082  */
21083
21084
21085 /**
21086  * The drag and drop utility provides a framework for building drag and drop
21087  * applications.  In addition to enabling drag and drop for specific elements,
21088  * the drag and drop elements are tracked by the manager class, and the
21089  * interactions between the various elements are tracked during the drag and
21090  * the implementing code is notified about these important moments.
21091  */
21092
21093 // Only load the library once.  Rewriting the manager class would orphan
21094 // existing drag and drop instances.
21095 if (!Roo.dd.DragDropMgr) {
21096
21097 /**
21098  * @class Roo.dd.DragDropMgr
21099  * DragDropMgr is a singleton that tracks the element interaction for
21100  * all DragDrop items in the window.  Generally, you will not call
21101  * this class directly, but it does have helper methods that could
21102  * be useful in your DragDrop implementations.
21103  * @static
21104  */
21105 Roo.dd.DragDropMgr = function() {
21106
21107     var Event = Roo.EventManager;
21108
21109     return {
21110
21111         /**
21112          * Two dimensional Array of registered DragDrop objects.  The first
21113          * dimension is the DragDrop item group, the second the DragDrop
21114          * object.
21115          * @property ids
21116          * @type {string: string}
21117          * @private
21118          * @static
21119          */
21120         ids: {},
21121
21122         /**
21123          * Array of element ids defined as drag handles.  Used to determine
21124          * if the element that generated the mousedown event is actually the
21125          * handle and not the html element itself.
21126          * @property handleIds
21127          * @type {string: string}
21128          * @private
21129          * @static
21130          */
21131         handleIds: {},
21132
21133         /**
21134          * the DragDrop object that is currently being dragged
21135          * @property dragCurrent
21136          * @type DragDrop
21137          * @private
21138          * @static
21139          **/
21140         dragCurrent: null,
21141
21142         /**
21143          * the DragDrop object(s) that are being hovered over
21144          * @property dragOvers
21145          * @type Array
21146          * @private
21147          * @static
21148          */
21149         dragOvers: {},
21150
21151         /**
21152          * the X distance between the cursor and the object being dragged
21153          * @property deltaX
21154          * @type int
21155          * @private
21156          * @static
21157          */
21158         deltaX: 0,
21159
21160         /**
21161          * the Y distance between the cursor and the object being dragged
21162          * @property deltaY
21163          * @type int
21164          * @private
21165          * @static
21166          */
21167         deltaY: 0,
21168
21169         /**
21170          * Flag to determine if we should prevent the default behavior of the
21171          * events we define. By default this is true, but this can be set to
21172          * false if you need the default behavior (not recommended)
21173          * @property preventDefault
21174          * @type boolean
21175          * @static
21176          */
21177         preventDefault: true,
21178
21179         /**
21180          * Flag to determine if we should stop the propagation of the events
21181          * we generate. This is true by default but you may want to set it to
21182          * false if the html element contains other features that require the
21183          * mouse click.
21184          * @property stopPropagation
21185          * @type boolean
21186          * @static
21187          */
21188         stopPropagation: true,
21189
21190         /**
21191          * Internal flag that is set to true when drag and drop has been
21192          * intialized
21193          * @property initialized
21194          * @private
21195          * @static
21196          */
21197         initalized: false,
21198
21199         /**
21200          * All drag and drop can be disabled.
21201          * @property locked
21202          * @private
21203          * @static
21204          */
21205         locked: false,
21206
21207         /**
21208          * Called the first time an element is registered.
21209          * @method init
21210          * @private
21211          * @static
21212          */
21213         init: function() {
21214             this.initialized = true;
21215         },
21216
21217         /**
21218          * In point mode, drag and drop interaction is defined by the
21219          * location of the cursor during the drag/drop
21220          * @property POINT
21221          * @type int
21222          * @static
21223          */
21224         POINT: 0,
21225
21226         /**
21227          * In intersect mode, drag and drop interactio nis defined by the
21228          * overlap of two or more drag and drop objects.
21229          * @property INTERSECT
21230          * @type int
21231          * @static
21232          */
21233         INTERSECT: 1,
21234
21235         /**
21236          * The current drag and drop mode.  Default: POINT
21237          * @property mode
21238          * @type int
21239          * @static
21240          */
21241         mode: 0,
21242
21243         /**
21244          * Runs method on all drag and drop objects
21245          * @method _execOnAll
21246          * @private
21247          * @static
21248          */
21249         _execOnAll: function(sMethod, args) {
21250             for (var i in this.ids) {
21251                 for (var j in this.ids[i]) {
21252                     var oDD = this.ids[i][j];
21253                     if (! this.isTypeOfDD(oDD)) {
21254                         continue;
21255                     }
21256                     oDD[sMethod].apply(oDD, args);
21257                 }
21258             }
21259         },
21260
21261         /**
21262          * Drag and drop initialization.  Sets up the global event handlers
21263          * @method _onLoad
21264          * @private
21265          * @static
21266          */
21267         _onLoad: function() {
21268
21269             this.init();
21270
21271             if (!Roo.isTouch) {
21272                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21273                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21274             }
21275             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21276             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21277             
21278             Event.on(window,   "unload",    this._onUnload, this, true);
21279             Event.on(window,   "resize",    this._onResize, this, true);
21280             // Event.on(window,   "mouseout",    this._test);
21281
21282         },
21283
21284         /**
21285          * Reset constraints on all drag and drop objs
21286          * @method _onResize
21287          * @private
21288          * @static
21289          */
21290         _onResize: function(e) {
21291             this._execOnAll("resetConstraints", []);
21292         },
21293
21294         /**
21295          * Lock all drag and drop functionality
21296          * @method lock
21297          * @static
21298          */
21299         lock: function() { this.locked = true; },
21300
21301         /**
21302          * Unlock all drag and drop functionality
21303          * @method unlock
21304          * @static
21305          */
21306         unlock: function() { this.locked = false; },
21307
21308         /**
21309          * Is drag and drop locked?
21310          * @method isLocked
21311          * @return {boolean} True if drag and drop is locked, false otherwise.
21312          * @static
21313          */
21314         isLocked: function() { return this.locked; },
21315
21316         /**
21317          * Location cache that is set for all drag drop objects when a drag is
21318          * initiated, cleared when the drag is finished.
21319          * @property locationCache
21320          * @private
21321          * @static
21322          */
21323         locationCache: {},
21324
21325         /**
21326          * Set useCache to false if you want to force object the lookup of each
21327          * drag and drop linked element constantly during a drag.
21328          * @property useCache
21329          * @type boolean
21330          * @static
21331          */
21332         useCache: true,
21333
21334         /**
21335          * The number of pixels that the mouse needs to move after the
21336          * mousedown before the drag is initiated.  Default=3;
21337          * @property clickPixelThresh
21338          * @type int
21339          * @static
21340          */
21341         clickPixelThresh: 3,
21342
21343         /**
21344          * The number of milliseconds after the mousedown event to initiate the
21345          * drag if we don't get a mouseup event. Default=1000
21346          * @property clickTimeThresh
21347          * @type int
21348          * @static
21349          */
21350         clickTimeThresh: 350,
21351
21352         /**
21353          * Flag that indicates that either the drag pixel threshold or the
21354          * mousdown time threshold has been met
21355          * @property dragThreshMet
21356          * @type boolean
21357          * @private
21358          * @static
21359          */
21360         dragThreshMet: false,
21361
21362         /**
21363          * Timeout used for the click time threshold
21364          * @property clickTimeout
21365          * @type Object
21366          * @private
21367          * @static
21368          */
21369         clickTimeout: null,
21370
21371         /**
21372          * The X position of the mousedown event stored for later use when a
21373          * drag threshold is met.
21374          * @property startX
21375          * @type int
21376          * @private
21377          * @static
21378          */
21379         startX: 0,
21380
21381         /**
21382          * The Y position of the mousedown event stored for later use when a
21383          * drag threshold is met.
21384          * @property startY
21385          * @type int
21386          * @private
21387          * @static
21388          */
21389         startY: 0,
21390
21391         /**
21392          * Each DragDrop instance must be registered with the DragDropMgr.
21393          * This is executed in DragDrop.init()
21394          * @method regDragDrop
21395          * @param {DragDrop} oDD the DragDrop object to register
21396          * @param {String} sGroup the name of the group this element belongs to
21397          * @static
21398          */
21399         regDragDrop: function(oDD, sGroup) {
21400             if (!this.initialized) { this.init(); }
21401
21402             if (!this.ids[sGroup]) {
21403                 this.ids[sGroup] = {};
21404             }
21405             this.ids[sGroup][oDD.id] = oDD;
21406         },
21407
21408         /**
21409          * Removes the supplied dd instance from the supplied group. Executed
21410          * by DragDrop.removeFromGroup, so don't call this function directly.
21411          * @method removeDDFromGroup
21412          * @private
21413          * @static
21414          */
21415         removeDDFromGroup: function(oDD, sGroup) {
21416             if (!this.ids[sGroup]) {
21417                 this.ids[sGroup] = {};
21418             }
21419
21420             var obj = this.ids[sGroup];
21421             if (obj && obj[oDD.id]) {
21422                 delete obj[oDD.id];
21423             }
21424         },
21425
21426         /**
21427          * Unregisters a drag and drop item.  This is executed in
21428          * DragDrop.unreg, use that method instead of calling this directly.
21429          * @method _remove
21430          * @private
21431          * @static
21432          */
21433         _remove: function(oDD) {
21434             for (var g in oDD.groups) {
21435                 if (g && this.ids[g][oDD.id]) {
21436                     delete this.ids[g][oDD.id];
21437                 }
21438             }
21439             delete this.handleIds[oDD.id];
21440         },
21441
21442         /**
21443          * Each DragDrop handle element must be registered.  This is done
21444          * automatically when executing DragDrop.setHandleElId()
21445          * @method regHandle
21446          * @param {String} sDDId the DragDrop id this element is a handle for
21447          * @param {String} sHandleId the id of the element that is the drag
21448          * handle
21449          * @static
21450          */
21451         regHandle: function(sDDId, sHandleId) {
21452             if (!this.handleIds[sDDId]) {
21453                 this.handleIds[sDDId] = {};
21454             }
21455             this.handleIds[sDDId][sHandleId] = sHandleId;
21456         },
21457
21458         /**
21459          * Utility function to determine if a given element has been
21460          * registered as a drag drop item.
21461          * @method isDragDrop
21462          * @param {String} id the element id to check
21463          * @return {boolean} true if this element is a DragDrop item,
21464          * false otherwise
21465          * @static
21466          */
21467         isDragDrop: function(id) {
21468             return ( this.getDDById(id) ) ? true : false;
21469         },
21470
21471         /**
21472          * Returns the drag and drop instances that are in all groups the
21473          * passed in instance belongs to.
21474          * @method getRelated
21475          * @param {DragDrop} p_oDD the obj to get related data for
21476          * @param {boolean} bTargetsOnly if true, only return targetable objs
21477          * @return {DragDrop[]} the related instances
21478          * @static
21479          */
21480         getRelated: function(p_oDD, bTargetsOnly) {
21481             var oDDs = [];
21482             for (var i in p_oDD.groups) {
21483                 for (j in this.ids[i]) {
21484                     var dd = this.ids[i][j];
21485                     if (! this.isTypeOfDD(dd)) {
21486                         continue;
21487                     }
21488                     if (!bTargetsOnly || dd.isTarget) {
21489                         oDDs[oDDs.length] = dd;
21490                     }
21491                 }
21492             }
21493
21494             return oDDs;
21495         },
21496
21497         /**
21498          * Returns true if the specified dd target is a legal target for
21499          * the specifice drag obj
21500          * @method isLegalTarget
21501          * @param {DragDrop} the drag obj
21502          * @param {DragDrop} the target
21503          * @return {boolean} true if the target is a legal target for the
21504          * dd obj
21505          * @static
21506          */
21507         isLegalTarget: function (oDD, oTargetDD) {
21508             var targets = this.getRelated(oDD, true);
21509             for (var i=0, len=targets.length;i<len;++i) {
21510                 if (targets[i].id == oTargetDD.id) {
21511                     return true;
21512                 }
21513             }
21514
21515             return false;
21516         },
21517
21518         /**
21519          * My goal is to be able to transparently determine if an object is
21520          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21521          * returns "object", oDD.constructor.toString() always returns
21522          * "DragDrop" and not the name of the subclass.  So for now it just
21523          * evaluates a well-known variable in DragDrop.
21524          * @method isTypeOfDD
21525          * @param {Object} the object to evaluate
21526          * @return {boolean} true if typeof oDD = DragDrop
21527          * @static
21528          */
21529         isTypeOfDD: function (oDD) {
21530             return (oDD && oDD.__ygDragDrop);
21531         },
21532
21533         /**
21534          * Utility function to determine if a given element has been
21535          * registered as a drag drop handle for the given Drag Drop object.
21536          * @method isHandle
21537          * @param {String} id the element id to check
21538          * @return {boolean} true if this element is a DragDrop handle, false
21539          * otherwise
21540          * @static
21541          */
21542         isHandle: function(sDDId, sHandleId) {
21543             return ( this.handleIds[sDDId] &&
21544                             this.handleIds[sDDId][sHandleId] );
21545         },
21546
21547         /**
21548          * Returns the DragDrop instance for a given id
21549          * @method getDDById
21550          * @param {String} id the id of the DragDrop object
21551          * @return {DragDrop} the drag drop object, null if it is not found
21552          * @static
21553          */
21554         getDDById: function(id) {
21555             for (var i in this.ids) {
21556                 if (this.ids[i][id]) {
21557                     return this.ids[i][id];
21558                 }
21559             }
21560             return null;
21561         },
21562
21563         /**
21564          * Fired after a registered DragDrop object gets the mousedown event.
21565          * Sets up the events required to track the object being dragged
21566          * @method handleMouseDown
21567          * @param {Event} e the event
21568          * @param oDD the DragDrop object being dragged
21569          * @private
21570          * @static
21571          */
21572         handleMouseDown: function(e, oDD) {
21573             if(Roo.QuickTips){
21574                 Roo.QuickTips.disable();
21575             }
21576             this.currentTarget = e.getTarget();
21577
21578             this.dragCurrent = oDD;
21579
21580             var el = oDD.getEl();
21581
21582             // track start position
21583             this.startX = e.getPageX();
21584             this.startY = e.getPageY();
21585
21586             this.deltaX = this.startX - el.offsetLeft;
21587             this.deltaY = this.startY - el.offsetTop;
21588
21589             this.dragThreshMet = false;
21590
21591             this.clickTimeout = setTimeout(
21592                     function() {
21593                         var DDM = Roo.dd.DDM;
21594                         DDM.startDrag(DDM.startX, DDM.startY);
21595                     },
21596                     this.clickTimeThresh );
21597         },
21598
21599         /**
21600          * Fired when either the drag pixel threshol or the mousedown hold
21601          * time threshold has been met.
21602          * @method startDrag
21603          * @param x {int} the X position of the original mousedown
21604          * @param y {int} the Y position of the original mousedown
21605          * @static
21606          */
21607         startDrag: function(x, y) {
21608             clearTimeout(this.clickTimeout);
21609             if (this.dragCurrent) {
21610                 this.dragCurrent.b4StartDrag(x, y);
21611                 this.dragCurrent.startDrag(x, y);
21612             }
21613             this.dragThreshMet = true;
21614         },
21615
21616         /**
21617          * Internal function to handle the mouseup event.  Will be invoked
21618          * from the context of the document.
21619          * @method handleMouseUp
21620          * @param {Event} e the event
21621          * @private
21622          * @static
21623          */
21624         handleMouseUp: function(e) {
21625
21626             if(Roo.QuickTips){
21627                 Roo.QuickTips.enable();
21628             }
21629             if (! this.dragCurrent) {
21630                 return;
21631             }
21632
21633             clearTimeout(this.clickTimeout);
21634
21635             if (this.dragThreshMet) {
21636                 this.fireEvents(e, true);
21637             } else {
21638             }
21639
21640             this.stopDrag(e);
21641
21642             this.stopEvent(e);
21643         },
21644
21645         /**
21646          * Utility to stop event propagation and event default, if these
21647          * features are turned on.
21648          * @method stopEvent
21649          * @param {Event} e the event as returned by this.getEvent()
21650          * @static
21651          */
21652         stopEvent: function(e){
21653             if(this.stopPropagation) {
21654                 e.stopPropagation();
21655             }
21656
21657             if (this.preventDefault) {
21658                 e.preventDefault();
21659             }
21660         },
21661
21662         /**
21663          * Internal function to clean up event handlers after the drag
21664          * operation is complete
21665          * @method stopDrag
21666          * @param {Event} e the event
21667          * @private
21668          * @static
21669          */
21670         stopDrag: function(e) {
21671             // Fire the drag end event for the item that was dragged
21672             if (this.dragCurrent) {
21673                 if (this.dragThreshMet) {
21674                     this.dragCurrent.b4EndDrag(e);
21675                     this.dragCurrent.endDrag(e);
21676                 }
21677
21678                 this.dragCurrent.onMouseUp(e);
21679             }
21680
21681             this.dragCurrent = null;
21682             this.dragOvers = {};
21683         },
21684
21685         /**
21686          * Internal function to handle the mousemove event.  Will be invoked
21687          * from the context of the html element.
21688          *
21689          * @TODO figure out what we can do about mouse events lost when the
21690          * user drags objects beyond the window boundary.  Currently we can
21691          * detect this in internet explorer by verifying that the mouse is
21692          * down during the mousemove event.  Firefox doesn't give us the
21693          * button state on the mousemove event.
21694          * @method handleMouseMove
21695          * @param {Event} e the event
21696          * @private
21697          * @static
21698          */
21699         handleMouseMove: function(e) {
21700             if (! this.dragCurrent) {
21701                 return true;
21702             }
21703
21704             // var button = e.which || e.button;
21705
21706             // check for IE mouseup outside of page boundary
21707             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21708                 this.stopEvent(e);
21709                 return this.handleMouseUp(e);
21710             }
21711
21712             if (!this.dragThreshMet) {
21713                 var diffX = Math.abs(this.startX - e.getPageX());
21714                 var diffY = Math.abs(this.startY - e.getPageY());
21715                 if (diffX > this.clickPixelThresh ||
21716                             diffY > this.clickPixelThresh) {
21717                     this.startDrag(this.startX, this.startY);
21718                 }
21719             }
21720
21721             if (this.dragThreshMet) {
21722                 this.dragCurrent.b4Drag(e);
21723                 this.dragCurrent.onDrag(e);
21724                 if(!this.dragCurrent.moveOnly){
21725                     this.fireEvents(e, false);
21726                 }
21727             }
21728
21729             this.stopEvent(e);
21730
21731             return true;
21732         },
21733
21734         /**
21735          * Iterates over all of the DragDrop elements to find ones we are
21736          * hovering over or dropping on
21737          * @method fireEvents
21738          * @param {Event} e the event
21739          * @param {boolean} isDrop is this a drop op or a mouseover op?
21740          * @private
21741          * @static
21742          */
21743         fireEvents: function(e, isDrop) {
21744             var dc = this.dragCurrent;
21745
21746             // If the user did the mouse up outside of the window, we could
21747             // get here even though we have ended the drag.
21748             if (!dc || dc.isLocked()) {
21749                 return;
21750             }
21751
21752             var pt = e.getPoint();
21753
21754             // cache the previous dragOver array
21755             var oldOvers = [];
21756
21757             var outEvts   = [];
21758             var overEvts  = [];
21759             var dropEvts  = [];
21760             var enterEvts = [];
21761
21762             // Check to see if the object(s) we were hovering over is no longer
21763             // being hovered over so we can fire the onDragOut event
21764             for (var i in this.dragOvers) {
21765
21766                 var ddo = this.dragOvers[i];
21767
21768                 if (! this.isTypeOfDD(ddo)) {
21769                     continue;
21770                 }
21771
21772                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21773                     outEvts.push( ddo );
21774                 }
21775
21776                 oldOvers[i] = true;
21777                 delete this.dragOvers[i];
21778             }
21779
21780             for (var sGroup in dc.groups) {
21781
21782                 if ("string" != typeof sGroup) {
21783                     continue;
21784                 }
21785
21786                 for (i in this.ids[sGroup]) {
21787                     var oDD = this.ids[sGroup][i];
21788                     if (! this.isTypeOfDD(oDD)) {
21789                         continue;
21790                     }
21791
21792                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21793                         if (this.isOverTarget(pt, oDD, this.mode)) {
21794                             // look for drop interactions
21795                             if (isDrop) {
21796                                 dropEvts.push( oDD );
21797                             // look for drag enter and drag over interactions
21798                             } else {
21799
21800                                 // initial drag over: dragEnter fires
21801                                 if (!oldOvers[oDD.id]) {
21802                                     enterEvts.push( oDD );
21803                                 // subsequent drag overs: dragOver fires
21804                                 } else {
21805                                     overEvts.push( oDD );
21806                                 }
21807
21808                                 this.dragOvers[oDD.id] = oDD;
21809                             }
21810                         }
21811                     }
21812                 }
21813             }
21814
21815             if (this.mode) {
21816                 if (outEvts.length) {
21817                     dc.b4DragOut(e, outEvts);
21818                     dc.onDragOut(e, outEvts);
21819                 }
21820
21821                 if (enterEvts.length) {
21822                     dc.onDragEnter(e, enterEvts);
21823                 }
21824
21825                 if (overEvts.length) {
21826                     dc.b4DragOver(e, overEvts);
21827                     dc.onDragOver(e, overEvts);
21828                 }
21829
21830                 if (dropEvts.length) {
21831                     dc.b4DragDrop(e, dropEvts);
21832                     dc.onDragDrop(e, dropEvts);
21833                 }
21834
21835             } else {
21836                 // fire dragout events
21837                 var len = 0;
21838                 for (i=0, len=outEvts.length; i<len; ++i) {
21839                     dc.b4DragOut(e, outEvts[i].id);
21840                     dc.onDragOut(e, outEvts[i].id);
21841                 }
21842
21843                 // fire enter events
21844                 for (i=0,len=enterEvts.length; i<len; ++i) {
21845                     // dc.b4DragEnter(e, oDD.id);
21846                     dc.onDragEnter(e, enterEvts[i].id);
21847                 }
21848
21849                 // fire over events
21850                 for (i=0,len=overEvts.length; i<len; ++i) {
21851                     dc.b4DragOver(e, overEvts[i].id);
21852                     dc.onDragOver(e, overEvts[i].id);
21853                 }
21854
21855                 // fire drop events
21856                 for (i=0, len=dropEvts.length; i<len; ++i) {
21857                     dc.b4DragDrop(e, dropEvts[i].id);
21858                     dc.onDragDrop(e, dropEvts[i].id);
21859                 }
21860
21861             }
21862
21863             // notify about a drop that did not find a target
21864             if (isDrop && !dropEvts.length) {
21865                 dc.onInvalidDrop(e);
21866             }
21867
21868         },
21869
21870         /**
21871          * Helper function for getting the best match from the list of drag
21872          * and drop objects returned by the drag and drop events when we are
21873          * in INTERSECT mode.  It returns either the first object that the
21874          * cursor is over, or the object that has the greatest overlap with
21875          * the dragged element.
21876          * @method getBestMatch
21877          * @param  {DragDrop[]} dds The array of drag and drop objects
21878          * targeted
21879          * @return {DragDrop}       The best single match
21880          * @static
21881          */
21882         getBestMatch: function(dds) {
21883             var winner = null;
21884             // Return null if the input is not what we expect
21885             //if (!dds || !dds.length || dds.length == 0) {
21886                // winner = null;
21887             // If there is only one item, it wins
21888             //} else if (dds.length == 1) {
21889
21890             var len = dds.length;
21891
21892             if (len == 1) {
21893                 winner = dds[0];
21894             } else {
21895                 // Loop through the targeted items
21896                 for (var i=0; i<len; ++i) {
21897                     var dd = dds[i];
21898                     // If the cursor is over the object, it wins.  If the
21899                     // cursor is over multiple matches, the first one we come
21900                     // to wins.
21901                     if (dd.cursorIsOver) {
21902                         winner = dd;
21903                         break;
21904                     // Otherwise the object with the most overlap wins
21905                     } else {
21906                         if (!winner ||
21907                             winner.overlap.getArea() < dd.overlap.getArea()) {
21908                             winner = dd;
21909                         }
21910                     }
21911                 }
21912             }
21913
21914             return winner;
21915         },
21916
21917         /**
21918          * Refreshes the cache of the top-left and bottom-right points of the
21919          * drag and drop objects in the specified group(s).  This is in the
21920          * format that is stored in the drag and drop instance, so typical
21921          * usage is:
21922          * <code>
21923          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21924          * </code>
21925          * Alternatively:
21926          * <code>
21927          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21928          * </code>
21929          * @TODO this really should be an indexed array.  Alternatively this
21930          * method could accept both.
21931          * @method refreshCache
21932          * @param {Object} groups an associative array of groups to refresh
21933          * @static
21934          */
21935         refreshCache: function(groups) {
21936             for (var sGroup in groups) {
21937                 if ("string" != typeof sGroup) {
21938                     continue;
21939                 }
21940                 for (var i in this.ids[sGroup]) {
21941                     var oDD = this.ids[sGroup][i];
21942
21943                     if (this.isTypeOfDD(oDD)) {
21944                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21945                         var loc = this.getLocation(oDD);
21946                         if (loc) {
21947                             this.locationCache[oDD.id] = loc;
21948                         } else {
21949                             delete this.locationCache[oDD.id];
21950                             // this will unregister the drag and drop object if
21951                             // the element is not in a usable state
21952                             // oDD.unreg();
21953                         }
21954                     }
21955                 }
21956             }
21957         },
21958
21959         /**
21960          * This checks to make sure an element exists and is in the DOM.  The
21961          * main purpose is to handle cases where innerHTML is used to remove
21962          * drag and drop objects from the DOM.  IE provides an 'unspecified
21963          * error' when trying to access the offsetParent of such an element
21964          * @method verifyEl
21965          * @param {HTMLElement} el the element to check
21966          * @return {boolean} true if the element looks usable
21967          * @static
21968          */
21969         verifyEl: function(el) {
21970             if (el) {
21971                 var parent;
21972                 if(Roo.isIE){
21973                     try{
21974                         parent = el.offsetParent;
21975                     }catch(e){}
21976                 }else{
21977                     parent = el.offsetParent;
21978                 }
21979                 if (parent) {
21980                     return true;
21981                 }
21982             }
21983
21984             return false;
21985         },
21986
21987         /**
21988          * Returns a Region object containing the drag and drop element's position
21989          * and size, including the padding configured for it
21990          * @method getLocation
21991          * @param {DragDrop} oDD the drag and drop object to get the
21992          *                       location for
21993          * @return {Roo.lib.Region} a Region object representing the total area
21994          *                             the element occupies, including any padding
21995          *                             the instance is configured for.
21996          * @static
21997          */
21998         getLocation: function(oDD) {
21999             if (! this.isTypeOfDD(oDD)) {
22000                 return null;
22001             }
22002
22003             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22004
22005             try {
22006                 pos= Roo.lib.Dom.getXY(el);
22007             } catch (e) { }
22008
22009             if (!pos) {
22010                 return null;
22011             }
22012
22013             x1 = pos[0];
22014             x2 = x1 + el.offsetWidth;
22015             y1 = pos[1];
22016             y2 = y1 + el.offsetHeight;
22017
22018             t = y1 - oDD.padding[0];
22019             r = x2 + oDD.padding[1];
22020             b = y2 + oDD.padding[2];
22021             l = x1 - oDD.padding[3];
22022
22023             return new Roo.lib.Region( t, r, b, l );
22024         },
22025
22026         /**
22027          * Checks the cursor location to see if it over the target
22028          * @method isOverTarget
22029          * @param {Roo.lib.Point} pt The point to evaluate
22030          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22031          * @return {boolean} true if the mouse is over the target
22032          * @private
22033          * @static
22034          */
22035         isOverTarget: function(pt, oTarget, intersect) {
22036             // use cache if available
22037             var loc = this.locationCache[oTarget.id];
22038             if (!loc || !this.useCache) {
22039                 loc = this.getLocation(oTarget);
22040                 this.locationCache[oTarget.id] = loc;
22041
22042             }
22043
22044             if (!loc) {
22045                 return false;
22046             }
22047
22048             oTarget.cursorIsOver = loc.contains( pt );
22049
22050             // DragDrop is using this as a sanity check for the initial mousedown
22051             // in this case we are done.  In POINT mode, if the drag obj has no
22052             // contraints, we are also done. Otherwise we need to evaluate the
22053             // location of the target as related to the actual location of the
22054             // dragged element.
22055             var dc = this.dragCurrent;
22056             if (!dc || !dc.getTargetCoord ||
22057                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22058                 return oTarget.cursorIsOver;
22059             }
22060
22061             oTarget.overlap = null;
22062
22063             // Get the current location of the drag element, this is the
22064             // location of the mouse event less the delta that represents
22065             // where the original mousedown happened on the element.  We
22066             // need to consider constraints and ticks as well.
22067             var pos = dc.getTargetCoord(pt.x, pt.y);
22068
22069             var el = dc.getDragEl();
22070             var curRegion = new Roo.lib.Region( pos.y,
22071                                                    pos.x + el.offsetWidth,
22072                                                    pos.y + el.offsetHeight,
22073                                                    pos.x );
22074
22075             var overlap = curRegion.intersect(loc);
22076
22077             if (overlap) {
22078                 oTarget.overlap = overlap;
22079                 return (intersect) ? true : oTarget.cursorIsOver;
22080             } else {
22081                 return false;
22082             }
22083         },
22084
22085         /**
22086          * unload event handler
22087          * @method _onUnload
22088          * @private
22089          * @static
22090          */
22091         _onUnload: function(e, me) {
22092             Roo.dd.DragDropMgr.unregAll();
22093         },
22094
22095         /**
22096          * Cleans up the drag and drop events and objects.
22097          * @method unregAll
22098          * @private
22099          * @static
22100          */
22101         unregAll: function() {
22102
22103             if (this.dragCurrent) {
22104                 this.stopDrag();
22105                 this.dragCurrent = null;
22106             }
22107
22108             this._execOnAll("unreg", []);
22109
22110             for (i in this.elementCache) {
22111                 delete this.elementCache[i];
22112             }
22113
22114             this.elementCache = {};
22115             this.ids = {};
22116         },
22117
22118         /**
22119          * A cache of DOM elements
22120          * @property elementCache
22121          * @private
22122          * @static
22123          */
22124         elementCache: {},
22125
22126         /**
22127          * Get the wrapper for the DOM element specified
22128          * @method getElWrapper
22129          * @param {String} id the id of the element to get
22130          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22131          * @private
22132          * @deprecated This wrapper isn't that useful
22133          * @static
22134          */
22135         getElWrapper: function(id) {
22136             var oWrapper = this.elementCache[id];
22137             if (!oWrapper || !oWrapper.el) {
22138                 oWrapper = this.elementCache[id] =
22139                     new this.ElementWrapper(Roo.getDom(id));
22140             }
22141             return oWrapper;
22142         },
22143
22144         /**
22145          * Returns the actual DOM element
22146          * @method getElement
22147          * @param {String} id the id of the elment to get
22148          * @return {Object} The element
22149          * @deprecated use Roo.getDom instead
22150          * @static
22151          */
22152         getElement: function(id) {
22153             return Roo.getDom(id);
22154         },
22155
22156         /**
22157          * Returns the style property for the DOM element (i.e.,
22158          * document.getElById(id).style)
22159          * @method getCss
22160          * @param {String} id the id of the elment to get
22161          * @return {Object} The style property of the element
22162          * @deprecated use Roo.getDom instead
22163          * @static
22164          */
22165         getCss: function(id) {
22166             var el = Roo.getDom(id);
22167             return (el) ? el.style : null;
22168         },
22169
22170         /**
22171          * Inner class for cached elements
22172          * @class DragDropMgr.ElementWrapper
22173          * @for DragDropMgr
22174          * @private
22175          * @deprecated
22176          */
22177         ElementWrapper: function(el) {
22178                 /**
22179                  * The element
22180                  * @property el
22181                  */
22182                 this.el = el || null;
22183                 /**
22184                  * The element id
22185                  * @property id
22186                  */
22187                 this.id = this.el && el.id;
22188                 /**
22189                  * A reference to the style property
22190                  * @property css
22191                  */
22192                 this.css = this.el && el.style;
22193             },
22194
22195         /**
22196          * Returns the X position of an html element
22197          * @method getPosX
22198          * @param el the element for which to get the position
22199          * @return {int} the X coordinate
22200          * @for DragDropMgr
22201          * @deprecated use Roo.lib.Dom.getX instead
22202          * @static
22203          */
22204         getPosX: function(el) {
22205             return Roo.lib.Dom.getX(el);
22206         },
22207
22208         /**
22209          * Returns the Y position of an html element
22210          * @method getPosY
22211          * @param el the element for which to get the position
22212          * @return {int} the Y coordinate
22213          * @deprecated use Roo.lib.Dom.getY instead
22214          * @static
22215          */
22216         getPosY: function(el) {
22217             return Roo.lib.Dom.getY(el);
22218         },
22219
22220         /**
22221          * Swap two nodes.  In IE, we use the native method, for others we
22222          * emulate the IE behavior
22223          * @method swapNode
22224          * @param n1 the first node to swap
22225          * @param n2 the other node to swap
22226          * @static
22227          */
22228         swapNode: function(n1, n2) {
22229             if (n1.swapNode) {
22230                 n1.swapNode(n2);
22231             } else {
22232                 var p = n2.parentNode;
22233                 var s = n2.nextSibling;
22234
22235                 if (s == n1) {
22236                     p.insertBefore(n1, n2);
22237                 } else if (n2 == n1.nextSibling) {
22238                     p.insertBefore(n2, n1);
22239                 } else {
22240                     n1.parentNode.replaceChild(n2, n1);
22241                     p.insertBefore(n1, s);
22242                 }
22243             }
22244         },
22245
22246         /**
22247          * Returns the current scroll position
22248          * @method getScroll
22249          * @private
22250          * @static
22251          */
22252         getScroll: function () {
22253             var t, l, dde=document.documentElement, db=document.body;
22254             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22255                 t = dde.scrollTop;
22256                 l = dde.scrollLeft;
22257             } else if (db) {
22258                 t = db.scrollTop;
22259                 l = db.scrollLeft;
22260             } else {
22261
22262             }
22263             return { top: t, left: l };
22264         },
22265
22266         /**
22267          * Returns the specified element style property
22268          * @method getStyle
22269          * @param {HTMLElement} el          the element
22270          * @param {string}      styleProp   the style property
22271          * @return {string} The value of the style property
22272          * @deprecated use Roo.lib.Dom.getStyle
22273          * @static
22274          */
22275         getStyle: function(el, styleProp) {
22276             return Roo.fly(el).getStyle(styleProp);
22277         },
22278
22279         /**
22280          * Gets the scrollTop
22281          * @method getScrollTop
22282          * @return {int} the document's scrollTop
22283          * @static
22284          */
22285         getScrollTop: function () { return this.getScroll().top; },
22286
22287         /**
22288          * Gets the scrollLeft
22289          * @method getScrollLeft
22290          * @return {int} the document's scrollTop
22291          * @static
22292          */
22293         getScrollLeft: function () { return this.getScroll().left; },
22294
22295         /**
22296          * Sets the x/y position of an element to the location of the
22297          * target element.
22298          * @method moveToEl
22299          * @param {HTMLElement} moveEl      The element to move
22300          * @param {HTMLElement} targetEl    The position reference element
22301          * @static
22302          */
22303         moveToEl: function (moveEl, targetEl) {
22304             var aCoord = Roo.lib.Dom.getXY(targetEl);
22305             Roo.lib.Dom.setXY(moveEl, aCoord);
22306         },
22307
22308         /**
22309          * Numeric array sort function
22310          * @method numericSort
22311          * @static
22312          */
22313         numericSort: function(a, b) { return (a - b); },
22314
22315         /**
22316          * Internal counter
22317          * @property _timeoutCount
22318          * @private
22319          * @static
22320          */
22321         _timeoutCount: 0,
22322
22323         /**
22324          * Trying to make the load order less important.  Without this we get
22325          * an error if this file is loaded before the Event Utility.
22326          * @method _addListeners
22327          * @private
22328          * @static
22329          */
22330         _addListeners: function() {
22331             var DDM = Roo.dd.DDM;
22332             if ( Roo.lib.Event && document ) {
22333                 DDM._onLoad();
22334             } else {
22335                 if (DDM._timeoutCount > 2000) {
22336                 } else {
22337                     setTimeout(DDM._addListeners, 10);
22338                     if (document && document.body) {
22339                         DDM._timeoutCount += 1;
22340                     }
22341                 }
22342             }
22343         },
22344
22345         /**
22346          * Recursively searches the immediate parent and all child nodes for
22347          * the handle element in order to determine wheter or not it was
22348          * clicked.
22349          * @method handleWasClicked
22350          * @param node the html element to inspect
22351          * @static
22352          */
22353         handleWasClicked: function(node, id) {
22354             if (this.isHandle(id, node.id)) {
22355                 return true;
22356             } else {
22357                 // check to see if this is a text node child of the one we want
22358                 var p = node.parentNode;
22359
22360                 while (p) {
22361                     if (this.isHandle(id, p.id)) {
22362                         return true;
22363                     } else {
22364                         p = p.parentNode;
22365                     }
22366                 }
22367             }
22368
22369             return false;
22370         }
22371
22372     };
22373
22374 }();
22375
22376 // shorter alias, save a few bytes
22377 Roo.dd.DDM = Roo.dd.DragDropMgr;
22378 Roo.dd.DDM._addListeners();
22379
22380 }/*
22381  * Based on:
22382  * Ext JS Library 1.1.1
22383  * Copyright(c) 2006-2007, Ext JS, LLC.
22384  *
22385  * Originally Released Under LGPL - original licence link has changed is not relivant.
22386  *
22387  * Fork - LGPL
22388  * <script type="text/javascript">
22389  */
22390
22391 /**
22392  * @class Roo.dd.DD
22393  * A DragDrop implementation where the linked element follows the
22394  * mouse cursor during a drag.
22395  * @extends Roo.dd.DragDrop
22396  * @constructor
22397  * @param {String} id the id of the linked element
22398  * @param {String} sGroup the group of related DragDrop items
22399  * @param {object} config an object containing configurable attributes
22400  *                Valid properties for DD:
22401  *                    scroll
22402  */
22403 Roo.dd.DD = function(id, sGroup, config) {
22404     if (id) {
22405         this.init(id, sGroup, config);
22406     }
22407 };
22408
22409 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22410
22411     /**
22412      * When set to true, the utility automatically tries to scroll the browser
22413      * window wehn a drag and drop element is dragged near the viewport boundary.
22414      * Defaults to true.
22415      * @property scroll
22416      * @type boolean
22417      */
22418     scroll: true,
22419
22420     /**
22421      * Sets the pointer offset to the distance between the linked element's top
22422      * left corner and the location the element was clicked
22423      * @method autoOffset
22424      * @param {int} iPageX the X coordinate of the click
22425      * @param {int} iPageY the Y coordinate of the click
22426      */
22427     autoOffset: function(iPageX, iPageY) {
22428         var x = iPageX - this.startPageX;
22429         var y = iPageY - this.startPageY;
22430         this.setDelta(x, y);
22431     },
22432
22433     /**
22434      * Sets the pointer offset.  You can call this directly to force the
22435      * offset to be in a particular location (e.g., pass in 0,0 to set it
22436      * to the center of the object)
22437      * @method setDelta
22438      * @param {int} iDeltaX the distance from the left
22439      * @param {int} iDeltaY the distance from the top
22440      */
22441     setDelta: function(iDeltaX, iDeltaY) {
22442         this.deltaX = iDeltaX;
22443         this.deltaY = iDeltaY;
22444     },
22445
22446     /**
22447      * Sets the drag element to the location of the mousedown or click event,
22448      * maintaining the cursor location relative to the location on the element
22449      * that was clicked.  Override this if you want to place the element in a
22450      * location other than where the cursor is.
22451      * @method setDragElPos
22452      * @param {int} iPageX the X coordinate of the mousedown or drag event
22453      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22454      */
22455     setDragElPos: function(iPageX, iPageY) {
22456         // the first time we do this, we are going to check to make sure
22457         // the element has css positioning
22458
22459         var el = this.getDragEl();
22460         this.alignElWithMouse(el, iPageX, iPageY);
22461     },
22462
22463     /**
22464      * Sets the element to the location of the mousedown or click event,
22465      * maintaining the cursor location relative to the location on the element
22466      * that was clicked.  Override this if you want to place the element in a
22467      * location other than where the cursor is.
22468      * @method alignElWithMouse
22469      * @param {HTMLElement} el the element to move
22470      * @param {int} iPageX the X coordinate of the mousedown or drag event
22471      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22472      */
22473     alignElWithMouse: function(el, iPageX, iPageY) {
22474         var oCoord = this.getTargetCoord(iPageX, iPageY);
22475         var fly = el.dom ? el : Roo.fly(el);
22476         if (!this.deltaSetXY) {
22477             var aCoord = [oCoord.x, oCoord.y];
22478             fly.setXY(aCoord);
22479             var newLeft = fly.getLeft(true);
22480             var newTop  = fly.getTop(true);
22481             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22482         } else {
22483             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22484         }
22485
22486         this.cachePosition(oCoord.x, oCoord.y);
22487         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22488         return oCoord;
22489     },
22490
22491     /**
22492      * Saves the most recent position so that we can reset the constraints and
22493      * tick marks on-demand.  We need to know this so that we can calculate the
22494      * number of pixels the element is offset from its original position.
22495      * @method cachePosition
22496      * @param iPageX the current x position (optional, this just makes it so we
22497      * don't have to look it up again)
22498      * @param iPageY the current y position (optional, this just makes it so we
22499      * don't have to look it up again)
22500      */
22501     cachePosition: function(iPageX, iPageY) {
22502         if (iPageX) {
22503             this.lastPageX = iPageX;
22504             this.lastPageY = iPageY;
22505         } else {
22506             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22507             this.lastPageX = aCoord[0];
22508             this.lastPageY = aCoord[1];
22509         }
22510     },
22511
22512     /**
22513      * Auto-scroll the window if the dragged object has been moved beyond the
22514      * visible window boundary.
22515      * @method autoScroll
22516      * @param {int} x the drag element's x position
22517      * @param {int} y the drag element's y position
22518      * @param {int} h the height of the drag element
22519      * @param {int} w the width of the drag element
22520      * @private
22521      */
22522     autoScroll: function(x, y, h, w) {
22523
22524         if (this.scroll) {
22525             // The client height
22526             var clientH = Roo.lib.Dom.getViewWidth();
22527
22528             // The client width
22529             var clientW = Roo.lib.Dom.getViewHeight();
22530
22531             // The amt scrolled down
22532             var st = this.DDM.getScrollTop();
22533
22534             // The amt scrolled right
22535             var sl = this.DDM.getScrollLeft();
22536
22537             // Location of the bottom of the element
22538             var bot = h + y;
22539
22540             // Location of the right of the element
22541             var right = w + x;
22542
22543             // The distance from the cursor to the bottom of the visible area,
22544             // adjusted so that we don't scroll if the cursor is beyond the
22545             // element drag constraints
22546             var toBot = (clientH + st - y - this.deltaY);
22547
22548             // The distance from the cursor to the right of the visible area
22549             var toRight = (clientW + sl - x - this.deltaX);
22550
22551
22552             // How close to the edge the cursor must be before we scroll
22553             // var thresh = (document.all) ? 100 : 40;
22554             var thresh = 40;
22555
22556             // How many pixels to scroll per autoscroll op.  This helps to reduce
22557             // clunky scrolling. IE is more sensitive about this ... it needs this
22558             // value to be higher.
22559             var scrAmt = (document.all) ? 80 : 30;
22560
22561             // Scroll down if we are near the bottom of the visible page and the
22562             // obj extends below the crease
22563             if ( bot > clientH && toBot < thresh ) {
22564                 window.scrollTo(sl, st + scrAmt);
22565             }
22566
22567             // Scroll up if the window is scrolled down and the top of the object
22568             // goes above the top border
22569             if ( y < st && st > 0 && y - st < thresh ) {
22570                 window.scrollTo(sl, st - scrAmt);
22571             }
22572
22573             // Scroll right if the obj is beyond the right border and the cursor is
22574             // near the border.
22575             if ( right > clientW && toRight < thresh ) {
22576                 window.scrollTo(sl + scrAmt, st);
22577             }
22578
22579             // Scroll left if the window has been scrolled to the right and the obj
22580             // extends past the left border
22581             if ( x < sl && sl > 0 && x - sl < thresh ) {
22582                 window.scrollTo(sl - scrAmt, st);
22583             }
22584         }
22585     },
22586
22587     /**
22588      * Finds the location the element should be placed if we want to move
22589      * it to where the mouse location less the click offset would place us.
22590      * @method getTargetCoord
22591      * @param {int} iPageX the X coordinate of the click
22592      * @param {int} iPageY the Y coordinate of the click
22593      * @return an object that contains the coordinates (Object.x and Object.y)
22594      * @private
22595      */
22596     getTargetCoord: function(iPageX, iPageY) {
22597
22598
22599         var x = iPageX - this.deltaX;
22600         var y = iPageY - this.deltaY;
22601
22602         if (this.constrainX) {
22603             if (x < this.minX) { x = this.minX; }
22604             if (x > this.maxX) { x = this.maxX; }
22605         }
22606
22607         if (this.constrainY) {
22608             if (y < this.minY) { y = this.minY; }
22609             if (y > this.maxY) { y = this.maxY; }
22610         }
22611
22612         x = this.getTick(x, this.xTicks);
22613         y = this.getTick(y, this.yTicks);
22614
22615
22616         return {x:x, y:y};
22617     },
22618
22619     /*
22620      * Sets up config options specific to this class. Overrides
22621      * Roo.dd.DragDrop, but all versions of this method through the
22622      * inheritance chain are called
22623      */
22624     applyConfig: function() {
22625         Roo.dd.DD.superclass.applyConfig.call(this);
22626         this.scroll = (this.config.scroll !== false);
22627     },
22628
22629     /*
22630      * Event that fires prior to the onMouseDown event.  Overrides
22631      * Roo.dd.DragDrop.
22632      */
22633     b4MouseDown: function(e) {
22634         // this.resetConstraints();
22635         this.autoOffset(e.getPageX(),
22636                             e.getPageY());
22637     },
22638
22639     /*
22640      * Event that fires prior to the onDrag event.  Overrides
22641      * Roo.dd.DragDrop.
22642      */
22643     b4Drag: function(e) {
22644         this.setDragElPos(e.getPageX(),
22645                             e.getPageY());
22646     },
22647
22648     toString: function() {
22649         return ("DD " + this.id);
22650     }
22651
22652     //////////////////////////////////////////////////////////////////////////
22653     // Debugging ygDragDrop events that can be overridden
22654     //////////////////////////////////////////////////////////////////////////
22655     /*
22656     startDrag: function(x, y) {
22657     },
22658
22659     onDrag: function(e) {
22660     },
22661
22662     onDragEnter: function(e, id) {
22663     },
22664
22665     onDragOver: function(e, id) {
22666     },
22667
22668     onDragOut: function(e, id) {
22669     },
22670
22671     onDragDrop: function(e, id) {
22672     },
22673
22674     endDrag: function(e) {
22675     }
22676
22677     */
22678
22679 });/*
22680  * Based on:
22681  * Ext JS Library 1.1.1
22682  * Copyright(c) 2006-2007, Ext JS, LLC.
22683  *
22684  * Originally Released Under LGPL - original licence link has changed is not relivant.
22685  *
22686  * Fork - LGPL
22687  * <script type="text/javascript">
22688  */
22689
22690 /**
22691  * @class Roo.dd.DDProxy
22692  * A DragDrop implementation that inserts an empty, bordered div into
22693  * the document that follows the cursor during drag operations.  At the time of
22694  * the click, the frame div is resized to the dimensions of the linked html
22695  * element, and moved to the exact location of the linked element.
22696  *
22697  * References to the "frame" element refer to the single proxy element that
22698  * was created to be dragged in place of all DDProxy elements on the
22699  * page.
22700  *
22701  * @extends Roo.dd.DD
22702  * @constructor
22703  * @param {String} id the id of the linked html element
22704  * @param {String} sGroup the group of related DragDrop objects
22705  * @param {object} config an object containing configurable attributes
22706  *                Valid properties for DDProxy in addition to those in DragDrop:
22707  *                   resizeFrame, centerFrame, dragElId
22708  */
22709 Roo.dd.DDProxy = function(id, sGroup, config) {
22710     if (id) {
22711         this.init(id, sGroup, config);
22712         this.initFrame();
22713     }
22714 };
22715
22716 /**
22717  * The default drag frame div id
22718  * @property Roo.dd.DDProxy.dragElId
22719  * @type String
22720  * @static
22721  */
22722 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22723
22724 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22725
22726     /**
22727      * By default we resize the drag frame to be the same size as the element
22728      * we want to drag (this is to get the frame effect).  We can turn it off
22729      * if we want a different behavior.
22730      * @property resizeFrame
22731      * @type boolean
22732      */
22733     resizeFrame: true,
22734
22735     /**
22736      * By default the frame is positioned exactly where the drag element is, so
22737      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22738      * you do not have constraints on the obj is to have the drag frame centered
22739      * around the cursor.  Set centerFrame to true for this effect.
22740      * @property centerFrame
22741      * @type boolean
22742      */
22743     centerFrame: false,
22744
22745     /**
22746      * Creates the proxy element if it does not yet exist
22747      * @method createFrame
22748      */
22749     createFrame: function() {
22750         var self = this;
22751         var body = document.body;
22752
22753         if (!body || !body.firstChild) {
22754             setTimeout( function() { self.createFrame(); }, 50 );
22755             return;
22756         }
22757
22758         var div = this.getDragEl();
22759
22760         if (!div) {
22761             div    = document.createElement("div");
22762             div.id = this.dragElId;
22763             var s  = div.style;
22764
22765             s.position   = "absolute";
22766             s.visibility = "hidden";
22767             s.cursor     = "move";
22768             s.border     = "2px solid #aaa";
22769             s.zIndex     = 999;
22770
22771             // appendChild can blow up IE if invoked prior to the window load event
22772             // while rendering a table.  It is possible there are other scenarios
22773             // that would cause this to happen as well.
22774             body.insertBefore(div, body.firstChild);
22775         }
22776     },
22777
22778     /**
22779      * Initialization for the drag frame element.  Must be called in the
22780      * constructor of all subclasses
22781      * @method initFrame
22782      */
22783     initFrame: function() {
22784         this.createFrame();
22785     },
22786
22787     applyConfig: function() {
22788         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22789
22790         this.resizeFrame = (this.config.resizeFrame !== false);
22791         this.centerFrame = (this.config.centerFrame);
22792         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22793     },
22794
22795     /**
22796      * Resizes the drag frame to the dimensions of the clicked object, positions
22797      * it over the object, and finally displays it
22798      * @method showFrame
22799      * @param {int} iPageX X click position
22800      * @param {int} iPageY Y click position
22801      * @private
22802      */
22803     showFrame: function(iPageX, iPageY) {
22804         var el = this.getEl();
22805         var dragEl = this.getDragEl();
22806         var s = dragEl.style;
22807
22808         this._resizeProxy();
22809
22810         if (this.centerFrame) {
22811             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22812                            Math.round(parseInt(s.height, 10)/2) );
22813         }
22814
22815         this.setDragElPos(iPageX, iPageY);
22816
22817         Roo.fly(dragEl).show();
22818     },
22819
22820     /**
22821      * The proxy is automatically resized to the dimensions of the linked
22822      * element when a drag is initiated, unless resizeFrame is set to false
22823      * @method _resizeProxy
22824      * @private
22825      */
22826     _resizeProxy: function() {
22827         if (this.resizeFrame) {
22828             var el = this.getEl();
22829             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22830         }
22831     },
22832
22833     // overrides Roo.dd.DragDrop
22834     b4MouseDown: function(e) {
22835         var x = e.getPageX();
22836         var y = e.getPageY();
22837         this.autoOffset(x, y);
22838         this.setDragElPos(x, y);
22839     },
22840
22841     // overrides Roo.dd.DragDrop
22842     b4StartDrag: function(x, y) {
22843         // show the drag frame
22844         this.showFrame(x, y);
22845     },
22846
22847     // overrides Roo.dd.DragDrop
22848     b4EndDrag: function(e) {
22849         Roo.fly(this.getDragEl()).hide();
22850     },
22851
22852     // overrides Roo.dd.DragDrop
22853     // By default we try to move the element to the last location of the frame.
22854     // This is so that the default behavior mirrors that of Roo.dd.DD.
22855     endDrag: function(e) {
22856
22857         var lel = this.getEl();
22858         var del = this.getDragEl();
22859
22860         // Show the drag frame briefly so we can get its position
22861         del.style.visibility = "";
22862
22863         this.beforeMove();
22864         // Hide the linked element before the move to get around a Safari
22865         // rendering bug.
22866         lel.style.visibility = "hidden";
22867         Roo.dd.DDM.moveToEl(lel, del);
22868         del.style.visibility = "hidden";
22869         lel.style.visibility = "";
22870
22871         this.afterDrag();
22872     },
22873
22874     beforeMove : function(){
22875
22876     },
22877
22878     afterDrag : function(){
22879
22880     },
22881
22882     toString: function() {
22883         return ("DDProxy " + this.id);
22884     }
22885
22886 });
22887 /*
22888  * Based on:
22889  * Ext JS Library 1.1.1
22890  * Copyright(c) 2006-2007, Ext JS, LLC.
22891  *
22892  * Originally Released Under LGPL - original licence link has changed is not relivant.
22893  *
22894  * Fork - LGPL
22895  * <script type="text/javascript">
22896  */
22897
22898  /**
22899  * @class Roo.dd.DDTarget
22900  * A DragDrop implementation that does not move, but can be a drop
22901  * target.  You would get the same result by simply omitting implementation
22902  * for the event callbacks, but this way we reduce the processing cost of the
22903  * event listener and the callbacks.
22904  * @extends Roo.dd.DragDrop
22905  * @constructor
22906  * @param {String} id the id of the element that is a drop target
22907  * @param {String} sGroup the group of related DragDrop objects
22908  * @param {object} config an object containing configurable attributes
22909  *                 Valid properties for DDTarget in addition to those in
22910  *                 DragDrop:
22911  *                    none
22912  */
22913 Roo.dd.DDTarget = function(id, sGroup, config) {
22914     if (id) {
22915         this.initTarget(id, sGroup, config);
22916     }
22917     if (config && (config.listeners || config.events)) { 
22918         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22919             listeners : config.listeners || {}, 
22920             events : config.events || {} 
22921         });    
22922     }
22923 };
22924
22925 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22926 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22927     toString: function() {
22928         return ("DDTarget " + this.id);
22929     }
22930 });
22931 /*
22932  * Based on:
22933  * Ext JS Library 1.1.1
22934  * Copyright(c) 2006-2007, Ext JS, LLC.
22935  *
22936  * Originally Released Under LGPL - original licence link has changed is not relivant.
22937  *
22938  * Fork - LGPL
22939  * <script type="text/javascript">
22940  */
22941  
22942
22943 /**
22944  * @class Roo.dd.ScrollManager
22945  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22946  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22947  * @static
22948  */
22949 Roo.dd.ScrollManager = function(){
22950     var ddm = Roo.dd.DragDropMgr;
22951     var els = {};
22952     var dragEl = null;
22953     var proc = {};
22954     
22955     
22956     
22957     var onStop = function(e){
22958         dragEl = null;
22959         clearProc();
22960     };
22961     
22962     var triggerRefresh = function(){
22963         if(ddm.dragCurrent){
22964              ddm.refreshCache(ddm.dragCurrent.groups);
22965         }
22966     };
22967     
22968     var doScroll = function(){
22969         if(ddm.dragCurrent){
22970             var dds = Roo.dd.ScrollManager;
22971             if(!dds.animate){
22972                 if(proc.el.scroll(proc.dir, dds.increment)){
22973                     triggerRefresh();
22974                 }
22975             }else{
22976                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22977             }
22978         }
22979     };
22980     
22981     var clearProc = function(){
22982         if(proc.id){
22983             clearInterval(proc.id);
22984         }
22985         proc.id = 0;
22986         proc.el = null;
22987         proc.dir = "";
22988     };
22989     
22990     var startProc = function(el, dir){
22991          Roo.log('scroll startproc');
22992         clearProc();
22993         proc.el = el;
22994         proc.dir = dir;
22995         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22996     };
22997     
22998     var onFire = function(e, isDrop){
22999        
23000         if(isDrop || !ddm.dragCurrent){ return; }
23001         var dds = Roo.dd.ScrollManager;
23002         if(!dragEl || dragEl != ddm.dragCurrent){
23003             dragEl = ddm.dragCurrent;
23004             // refresh regions on drag start
23005             dds.refreshCache();
23006         }
23007         
23008         var xy = Roo.lib.Event.getXY(e);
23009         var pt = new Roo.lib.Point(xy[0], xy[1]);
23010         for(var id in els){
23011             var el = els[id], r = el._region;
23012             if(r && r.contains(pt) && el.isScrollable()){
23013                 if(r.bottom - pt.y <= dds.thresh){
23014                     if(proc.el != el){
23015                         startProc(el, "down");
23016                     }
23017                     return;
23018                 }else if(r.right - pt.x <= dds.thresh){
23019                     if(proc.el != el){
23020                         startProc(el, "left");
23021                     }
23022                     return;
23023                 }else if(pt.y - r.top <= dds.thresh){
23024                     if(proc.el != el){
23025                         startProc(el, "up");
23026                     }
23027                     return;
23028                 }else if(pt.x - r.left <= dds.thresh){
23029                     if(proc.el != el){
23030                         startProc(el, "right");
23031                     }
23032                     return;
23033                 }
23034             }
23035         }
23036         clearProc();
23037     };
23038     
23039     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23040     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23041     
23042     return {
23043         /**
23044          * Registers new overflow element(s) to auto scroll
23045          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23046          */
23047         register : function(el){
23048             if(el instanceof Array){
23049                 for(var i = 0, len = el.length; i < len; i++) {
23050                         this.register(el[i]);
23051                 }
23052             }else{
23053                 el = Roo.get(el);
23054                 els[el.id] = el;
23055             }
23056             Roo.dd.ScrollManager.els = els;
23057         },
23058         
23059         /**
23060          * Unregisters overflow element(s) so they are no longer scrolled
23061          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23062          */
23063         unregister : function(el){
23064             if(el instanceof Array){
23065                 for(var i = 0, len = el.length; i < len; i++) {
23066                         this.unregister(el[i]);
23067                 }
23068             }else{
23069                 el = Roo.get(el);
23070                 delete els[el.id];
23071             }
23072         },
23073         
23074         /**
23075          * The number of pixels from the edge of a container the pointer needs to be to 
23076          * trigger scrolling (defaults to 25)
23077          * @type Number
23078          */
23079         thresh : 25,
23080         
23081         /**
23082          * The number of pixels to scroll in each scroll increment (defaults to 50)
23083          * @type Number
23084          */
23085         increment : 100,
23086         
23087         /**
23088          * The frequency of scrolls in milliseconds (defaults to 500)
23089          * @type Number
23090          */
23091         frequency : 500,
23092         
23093         /**
23094          * True to animate the scroll (defaults to true)
23095          * @type Boolean
23096          */
23097         animate: true,
23098         
23099         /**
23100          * The animation duration in seconds - 
23101          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23102          * @type Number
23103          */
23104         animDuration: .4,
23105         
23106         /**
23107          * Manually trigger a cache refresh.
23108          */
23109         refreshCache : function(){
23110             for(var id in els){
23111                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23112                     els[id]._region = els[id].getRegion();
23113                 }
23114             }
23115         }
23116     };
23117 }();/*
23118  * Based on:
23119  * Ext JS Library 1.1.1
23120  * Copyright(c) 2006-2007, Ext JS, LLC.
23121  *
23122  * Originally Released Under LGPL - original licence link has changed is not relivant.
23123  *
23124  * Fork - LGPL
23125  * <script type="text/javascript">
23126  */
23127  
23128
23129 /**
23130  * @class Roo.dd.Registry
23131  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23132  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23133  * @static
23134  */
23135 Roo.dd.Registry = function(){
23136     var elements = {}; 
23137     var handles = {}; 
23138     var autoIdSeed = 0;
23139
23140     var getId = function(el, autogen){
23141         if(typeof el == "string"){
23142             return el;
23143         }
23144         var id = el.id;
23145         if(!id && autogen !== false){
23146             id = "roodd-" + (++autoIdSeed);
23147             el.id = id;
23148         }
23149         return id;
23150     };
23151     
23152     return {
23153     /**
23154      * Register a drag drop element
23155      * @param {String|HTMLElement} element The id or DOM node to register
23156      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23157      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23158      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23159      * populated in the data object (if applicable):
23160      * <pre>
23161 Value      Description<br />
23162 ---------  ------------------------------------------<br />
23163 handles    Array of DOM nodes that trigger dragging<br />
23164            for the element being registered<br />
23165 isHandle   True if the element passed in triggers<br />
23166            dragging itself, else false
23167 </pre>
23168      */
23169         register : function(el, data){
23170             data = data || {};
23171             if(typeof el == "string"){
23172                 el = document.getElementById(el);
23173             }
23174             data.ddel = el;
23175             elements[getId(el)] = data;
23176             if(data.isHandle !== false){
23177                 handles[data.ddel.id] = data;
23178             }
23179             if(data.handles){
23180                 var hs = data.handles;
23181                 for(var i = 0, len = hs.length; i < len; i++){
23182                         handles[getId(hs[i])] = data;
23183                 }
23184             }
23185         },
23186
23187     /**
23188      * Unregister a drag drop element
23189      * @param {String|HTMLElement}  element The id or DOM node to unregister
23190      */
23191         unregister : function(el){
23192             var id = getId(el, false);
23193             var data = elements[id];
23194             if(data){
23195                 delete elements[id];
23196                 if(data.handles){
23197                     var hs = data.handles;
23198                     for(var i = 0, len = hs.length; i < len; i++){
23199                         delete handles[getId(hs[i], false)];
23200                     }
23201                 }
23202             }
23203         },
23204
23205     /**
23206      * Returns the handle registered for a DOM Node by id
23207      * @param {String|HTMLElement} id The DOM node or id to look up
23208      * @return {Object} handle The custom handle data
23209      */
23210         getHandle : function(id){
23211             if(typeof id != "string"){ // must be element?
23212                 id = id.id;
23213             }
23214             return handles[id];
23215         },
23216
23217     /**
23218      * Returns the handle that is registered for the DOM node that is the target of the event
23219      * @param {Event} e The event
23220      * @return {Object} handle The custom handle data
23221      */
23222         getHandleFromEvent : function(e){
23223             var t = Roo.lib.Event.getTarget(e);
23224             return t ? handles[t.id] : null;
23225         },
23226
23227     /**
23228      * Returns a custom data object that is registered for a DOM node by id
23229      * @param {String|HTMLElement} id The DOM node or id to look up
23230      * @return {Object} data The custom data
23231      */
23232         getTarget : function(id){
23233             if(typeof id != "string"){ // must be element?
23234                 id = id.id;
23235             }
23236             return elements[id];
23237         },
23238
23239     /**
23240      * Returns a custom data object that is registered for the DOM node that is the target of the event
23241      * @param {Event} e The event
23242      * @return {Object} data The custom data
23243      */
23244         getTargetFromEvent : function(e){
23245             var t = Roo.lib.Event.getTarget(e);
23246             return t ? elements[t.id] || handles[t.id] : null;
23247         }
23248     };
23249 }();/*
23250  * Based on:
23251  * Ext JS Library 1.1.1
23252  * Copyright(c) 2006-2007, Ext JS, LLC.
23253  *
23254  * Originally Released Under LGPL - original licence link has changed is not relivant.
23255  *
23256  * Fork - LGPL
23257  * <script type="text/javascript">
23258  */
23259  
23260
23261 /**
23262  * @class Roo.dd.StatusProxy
23263  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23264  * default drag proxy used by all Roo.dd components.
23265  * @constructor
23266  * @param {Object} config
23267  */
23268 Roo.dd.StatusProxy = function(config){
23269     Roo.apply(this, config);
23270     this.id = this.id || Roo.id();
23271     this.el = new Roo.Layer({
23272         dh: {
23273             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23274                 {tag: "div", cls: "x-dd-drop-icon"},
23275                 {tag: "div", cls: "x-dd-drag-ghost"}
23276             ]
23277         }, 
23278         shadow: !config || config.shadow !== false
23279     });
23280     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23281     this.dropStatus = this.dropNotAllowed;
23282 };
23283
23284 Roo.dd.StatusProxy.prototype = {
23285     /**
23286      * @cfg {String} dropAllowed
23287      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23288      */
23289     dropAllowed : "x-dd-drop-ok",
23290     /**
23291      * @cfg {String} dropNotAllowed
23292      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23293      */
23294     dropNotAllowed : "x-dd-drop-nodrop",
23295
23296     /**
23297      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23298      * over the current target element.
23299      * @param {String} cssClass The css class for the new drop status indicator image
23300      */
23301     setStatus : function(cssClass){
23302         cssClass = cssClass || this.dropNotAllowed;
23303         if(this.dropStatus != cssClass){
23304             this.el.replaceClass(this.dropStatus, cssClass);
23305             this.dropStatus = cssClass;
23306         }
23307     },
23308
23309     /**
23310      * Resets the status indicator to the default dropNotAllowed value
23311      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23312      */
23313     reset : function(clearGhost){
23314         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23315         this.dropStatus = this.dropNotAllowed;
23316         if(clearGhost){
23317             this.ghost.update("");
23318         }
23319     },
23320
23321     /**
23322      * Updates the contents of the ghost element
23323      * @param {String} html The html that will replace the current innerHTML of the ghost element
23324      */
23325     update : function(html){
23326         if(typeof html == "string"){
23327             this.ghost.update(html);
23328         }else{
23329             this.ghost.update("");
23330             html.style.margin = "0";
23331             this.ghost.dom.appendChild(html);
23332         }
23333         // ensure float = none set?? cant remember why though.
23334         var el = this.ghost.dom.firstChild;
23335                 if(el){
23336                         Roo.fly(el).setStyle('float', 'none');
23337                 }
23338     },
23339     
23340     /**
23341      * Returns the underlying proxy {@link Roo.Layer}
23342      * @return {Roo.Layer} el
23343     */
23344     getEl : function(){
23345         return this.el;
23346     },
23347
23348     /**
23349      * Returns the ghost element
23350      * @return {Roo.Element} el
23351      */
23352     getGhost : function(){
23353         return this.ghost;
23354     },
23355
23356     /**
23357      * Hides the proxy
23358      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23359      */
23360     hide : function(clear){
23361         this.el.hide();
23362         if(clear){
23363             this.reset(true);
23364         }
23365     },
23366
23367     /**
23368      * Stops the repair animation if it's currently running
23369      */
23370     stop : function(){
23371         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23372             this.anim.stop();
23373         }
23374     },
23375
23376     /**
23377      * Displays this proxy
23378      */
23379     show : function(){
23380         this.el.show();
23381     },
23382
23383     /**
23384      * Force the Layer to sync its shadow and shim positions to the element
23385      */
23386     sync : function(){
23387         this.el.sync();
23388     },
23389
23390     /**
23391      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23392      * invalid drop operation by the item being dragged.
23393      * @param {Array} xy The XY position of the element ([x, y])
23394      * @param {Function} callback The function to call after the repair is complete
23395      * @param {Object} scope The scope in which to execute the callback
23396      */
23397     repair : function(xy, callback, scope){
23398         this.callback = callback;
23399         this.scope = scope;
23400         if(xy && this.animRepair !== false){
23401             this.el.addClass("x-dd-drag-repair");
23402             this.el.hideUnders(true);
23403             this.anim = this.el.shift({
23404                 duration: this.repairDuration || .5,
23405                 easing: 'easeOut',
23406                 xy: xy,
23407                 stopFx: true,
23408                 callback: this.afterRepair,
23409                 scope: this
23410             });
23411         }else{
23412             this.afterRepair();
23413         }
23414     },
23415
23416     // private
23417     afterRepair : function(){
23418         this.hide(true);
23419         if(typeof this.callback == "function"){
23420             this.callback.call(this.scope || this);
23421         }
23422         this.callback = null;
23423         this.scope = null;
23424     }
23425 };/*
23426  * Based on:
23427  * Ext JS Library 1.1.1
23428  * Copyright(c) 2006-2007, Ext JS, LLC.
23429  *
23430  * Originally Released Under LGPL - original licence link has changed is not relivant.
23431  *
23432  * Fork - LGPL
23433  * <script type="text/javascript">
23434  */
23435
23436 /**
23437  * @class Roo.dd.DragSource
23438  * @extends Roo.dd.DDProxy
23439  * A simple class that provides the basic implementation needed to make any element draggable.
23440  * @constructor
23441  * @param {String/HTMLElement/Element} el The container element
23442  * @param {Object} config
23443  */
23444 Roo.dd.DragSource = function(el, config){
23445     this.el = Roo.get(el);
23446     this.dragData = {};
23447     
23448     Roo.apply(this, config);
23449     
23450     if(!this.proxy){
23451         this.proxy = new Roo.dd.StatusProxy();
23452     }
23453
23454     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23455           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23456     
23457     this.dragging = false;
23458 };
23459
23460 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23461     /**
23462      * @cfg {String} dropAllowed
23463      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23464      */
23465     dropAllowed : "x-dd-drop-ok",
23466     /**
23467      * @cfg {String} dropNotAllowed
23468      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23469      */
23470     dropNotAllowed : "x-dd-drop-nodrop",
23471
23472     /**
23473      * Returns the data object associated with this drag source
23474      * @return {Object} data An object containing arbitrary data
23475      */
23476     getDragData : function(e){
23477         return this.dragData;
23478     },
23479
23480     // private
23481     onDragEnter : function(e, id){
23482         var target = Roo.dd.DragDropMgr.getDDById(id);
23483         this.cachedTarget = target;
23484         if(this.beforeDragEnter(target, e, id) !== false){
23485             if(target.isNotifyTarget){
23486                 var status = target.notifyEnter(this, e, this.dragData);
23487                 this.proxy.setStatus(status);
23488             }else{
23489                 this.proxy.setStatus(this.dropAllowed);
23490             }
23491             
23492             if(this.afterDragEnter){
23493                 /**
23494                  * An empty function by default, but provided so that you can perform a custom action
23495                  * when the dragged item enters the drop target by providing an implementation.
23496                  * @param {Roo.dd.DragDrop} target The drop target
23497                  * @param {Event} e The event object
23498                  * @param {String} id The id of the dragged element
23499                  * @method afterDragEnter
23500                  */
23501                 this.afterDragEnter(target, e, id);
23502             }
23503         }
23504     },
23505
23506     /**
23507      * An empty function by default, but provided so that you can perform a custom action
23508      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23509      * @param {Roo.dd.DragDrop} target The drop target
23510      * @param {Event} e The event object
23511      * @param {String} id The id of the dragged element
23512      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23513      */
23514     beforeDragEnter : function(target, e, id){
23515         return true;
23516     },
23517
23518     // private
23519     alignElWithMouse: function() {
23520         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23521         this.proxy.sync();
23522     },
23523
23524     // private
23525     onDragOver : function(e, id){
23526         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23527         if(this.beforeDragOver(target, e, id) !== false){
23528             if(target.isNotifyTarget){
23529                 var status = target.notifyOver(this, e, this.dragData);
23530                 this.proxy.setStatus(status);
23531             }
23532
23533             if(this.afterDragOver){
23534                 /**
23535                  * An empty function by default, but provided so that you can perform a custom action
23536                  * while the dragged item is over the drop target by providing an implementation.
23537                  * @param {Roo.dd.DragDrop} target The drop target
23538                  * @param {Event} e The event object
23539                  * @param {String} id The id of the dragged element
23540                  * @method afterDragOver
23541                  */
23542                 this.afterDragOver(target, e, id);
23543             }
23544         }
23545     },
23546
23547     /**
23548      * An empty function by default, but provided so that you can perform a custom action
23549      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23550      * @param {Roo.dd.DragDrop} target The drop target
23551      * @param {Event} e The event object
23552      * @param {String} id The id of the dragged element
23553      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23554      */
23555     beforeDragOver : function(target, e, id){
23556         return true;
23557     },
23558
23559     // private
23560     onDragOut : function(e, id){
23561         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23562         if(this.beforeDragOut(target, e, id) !== false){
23563             if(target.isNotifyTarget){
23564                 target.notifyOut(this, e, this.dragData);
23565             }
23566             this.proxy.reset();
23567             if(this.afterDragOut){
23568                 /**
23569                  * An empty function by default, but provided so that you can perform a custom action
23570                  * after the dragged item is dragged out of the target without dropping.
23571                  * @param {Roo.dd.DragDrop} target The drop target
23572                  * @param {Event} e The event object
23573                  * @param {String} id The id of the dragged element
23574                  * @method afterDragOut
23575                  */
23576                 this.afterDragOut(target, e, id);
23577             }
23578         }
23579         this.cachedTarget = null;
23580     },
23581
23582     /**
23583      * An empty function by default, but provided so that you can perform a custom action before the dragged
23584      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23585      * @param {Roo.dd.DragDrop} target The drop target
23586      * @param {Event} e The event object
23587      * @param {String} id The id of the dragged element
23588      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23589      */
23590     beforeDragOut : function(target, e, id){
23591         return true;
23592     },
23593     
23594     // private
23595     onDragDrop : function(e, id){
23596         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23597         if(this.beforeDragDrop(target, e, id) !== false){
23598             if(target.isNotifyTarget){
23599                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23600                     this.onValidDrop(target, e, id);
23601                 }else{
23602                     this.onInvalidDrop(target, e, id);
23603                 }
23604             }else{
23605                 this.onValidDrop(target, e, id);
23606             }
23607             
23608             if(this.afterDragDrop){
23609                 /**
23610                  * An empty function by default, but provided so that you can perform a custom action
23611                  * after a valid drag drop has occurred by providing an implementation.
23612                  * @param {Roo.dd.DragDrop} target The drop target
23613                  * @param {Event} e The event object
23614                  * @param {String} id The id of the dropped element
23615                  * @method afterDragDrop
23616                  */
23617                 this.afterDragDrop(target, e, id);
23618             }
23619         }
23620         delete this.cachedTarget;
23621     },
23622
23623     /**
23624      * An empty function by default, but provided so that you can perform a custom action before the dragged
23625      * item is dropped onto the target and optionally cancel the onDragDrop.
23626      * @param {Roo.dd.DragDrop} target The drop target
23627      * @param {Event} e The event object
23628      * @param {String} id The id of the dragged element
23629      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23630      */
23631     beforeDragDrop : function(target, e, id){
23632         return true;
23633     },
23634
23635     // private
23636     onValidDrop : function(target, e, id){
23637         this.hideProxy();
23638         if(this.afterValidDrop){
23639             /**
23640              * An empty function by default, but provided so that you can perform a custom action
23641              * after a valid drop has occurred by providing an implementation.
23642              * @param {Object} target The target DD 
23643              * @param {Event} e The event object
23644              * @param {String} id The id of the dropped element
23645              * @method afterInvalidDrop
23646              */
23647             this.afterValidDrop(target, e, id);
23648         }
23649     },
23650
23651     // private
23652     getRepairXY : function(e, data){
23653         return this.el.getXY();  
23654     },
23655
23656     // private
23657     onInvalidDrop : function(target, e, id){
23658         this.beforeInvalidDrop(target, e, id);
23659         if(this.cachedTarget){
23660             if(this.cachedTarget.isNotifyTarget){
23661                 this.cachedTarget.notifyOut(this, e, this.dragData);
23662             }
23663             this.cacheTarget = null;
23664         }
23665         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23666
23667         if(this.afterInvalidDrop){
23668             /**
23669              * An empty function by default, but provided so that you can perform a custom action
23670              * after an invalid drop has occurred by providing an implementation.
23671              * @param {Event} e The event object
23672              * @param {String} id The id of the dropped element
23673              * @method afterInvalidDrop
23674              */
23675             this.afterInvalidDrop(e, id);
23676         }
23677     },
23678
23679     // private
23680     afterRepair : function(){
23681         if(Roo.enableFx){
23682             this.el.highlight(this.hlColor || "c3daf9");
23683         }
23684         this.dragging = false;
23685     },
23686
23687     /**
23688      * An empty function by default, but provided so that you can perform a custom action after an invalid
23689      * drop has occurred.
23690      * @param {Roo.dd.DragDrop} target The drop target
23691      * @param {Event} e The event object
23692      * @param {String} id The id of the dragged element
23693      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23694      */
23695     beforeInvalidDrop : function(target, e, id){
23696         return true;
23697     },
23698
23699     // private
23700     handleMouseDown : function(e){
23701         if(this.dragging) {
23702             return;
23703         }
23704         var data = this.getDragData(e);
23705         if(data && this.onBeforeDrag(data, e) !== false){
23706             this.dragData = data;
23707             this.proxy.stop();
23708             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23709         } 
23710     },
23711
23712     /**
23713      * An empty function by default, but provided so that you can perform a custom action before the initial
23714      * drag event begins and optionally cancel it.
23715      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23716      * @param {Event} e The event object
23717      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23718      */
23719     onBeforeDrag : function(data, e){
23720         return true;
23721     },
23722
23723     /**
23724      * An empty function by default, but provided so that you can perform a custom action once the initial
23725      * drag event has begun.  The drag cannot be canceled from this function.
23726      * @param {Number} x The x position of the click on the dragged object
23727      * @param {Number} y The y position of the click on the dragged object
23728      */
23729     onStartDrag : Roo.emptyFn,
23730
23731     // private - YUI override
23732     startDrag : function(x, y){
23733         this.proxy.reset();
23734         this.dragging = true;
23735         this.proxy.update("");
23736         this.onInitDrag(x, y);
23737         this.proxy.show();
23738     },
23739
23740     // private
23741     onInitDrag : function(x, y){
23742         var clone = this.el.dom.cloneNode(true);
23743         clone.id = Roo.id(); // prevent duplicate ids
23744         this.proxy.update(clone);
23745         this.onStartDrag(x, y);
23746         return true;
23747     },
23748
23749     /**
23750      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23751      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23752      */
23753     getProxy : function(){
23754         return this.proxy;  
23755     },
23756
23757     /**
23758      * Hides the drag source's {@link Roo.dd.StatusProxy}
23759      */
23760     hideProxy : function(){
23761         this.proxy.hide();  
23762         this.proxy.reset(true);
23763         this.dragging = false;
23764     },
23765
23766     // private
23767     triggerCacheRefresh : function(){
23768         Roo.dd.DDM.refreshCache(this.groups);
23769     },
23770
23771     // private - override to prevent hiding
23772     b4EndDrag: function(e) {
23773     },
23774
23775     // private - override to prevent moving
23776     endDrag : function(e){
23777         this.onEndDrag(this.dragData, e);
23778     },
23779
23780     // private
23781     onEndDrag : function(data, e){
23782     },
23783     
23784     // private - pin to cursor
23785     autoOffset : function(x, y) {
23786         this.setDelta(-12, -20);
23787     }    
23788 });/*
23789  * Based on:
23790  * Ext JS Library 1.1.1
23791  * Copyright(c) 2006-2007, Ext JS, LLC.
23792  *
23793  * Originally Released Under LGPL - original licence link has changed is not relivant.
23794  *
23795  * Fork - LGPL
23796  * <script type="text/javascript">
23797  */
23798
23799
23800 /**
23801  * @class Roo.dd.DropTarget
23802  * @extends Roo.dd.DDTarget
23803  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23804  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23805  * @constructor
23806  * @param {String/HTMLElement/Element} el The container element
23807  * @param {Object} config
23808  */
23809 Roo.dd.DropTarget = function(el, config){
23810     this.el = Roo.get(el);
23811     
23812     var listeners = false; ;
23813     if (config && config.listeners) {
23814         listeners= config.listeners;
23815         delete config.listeners;
23816     }
23817     Roo.apply(this, config);
23818     
23819     if(this.containerScroll){
23820         Roo.dd.ScrollManager.register(this.el);
23821     }
23822     this.addEvents( {
23823          /**
23824          * @scope Roo.dd.DropTarget
23825          */
23826          
23827          /**
23828          * @event enter
23829          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23830          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23831          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23832          * 
23833          * IMPORTANT : it should set  this.valid to true|false
23834          * 
23835          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23836          * @param {Event} e The event
23837          * @param {Object} data An object containing arbitrary data supplied by the drag source
23838          */
23839         "enter" : true,
23840         
23841          /**
23842          * @event over
23843          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23844          * This method will be called on every mouse movement while the drag source is over the drop target.
23845          * This default implementation simply returns the dropAllowed config value.
23846          * 
23847          * IMPORTANT : it should set  this.valid to true|false
23848          * 
23849          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23850          * @param {Event} e The event
23851          * @param {Object} data An object containing arbitrary data supplied by the drag source
23852          
23853          */
23854         "over" : true,
23855         /**
23856          * @event out
23857          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23858          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23859          * overClass (if any) from the drop element.
23860          * 
23861          * 
23862          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23863          * @param {Event} e The event
23864          * @param {Object} data An object containing arbitrary data supplied by the drag source
23865          */
23866          "out" : true,
23867          
23868         /**
23869          * @event drop
23870          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23871          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23872          * implementation that does something to process the drop event and returns true so that the drag source's
23873          * repair action does not run.
23874          * 
23875          * IMPORTANT : it should set this.success
23876          * 
23877          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23878          * @param {Event} e The event
23879          * @param {Object} data An object containing arbitrary data supplied by the drag source
23880         */
23881          "drop" : true
23882     });
23883             
23884      
23885     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23886         this.el.dom, 
23887         this.ddGroup || this.group,
23888         {
23889             isTarget: true,
23890             listeners : listeners || {} 
23891            
23892         
23893         }
23894     );
23895
23896 };
23897
23898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23899     /**
23900      * @cfg {String} overClass
23901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23902      */
23903      /**
23904      * @cfg {String} ddGroup
23905      * The drag drop group to handle drop events for
23906      */
23907      
23908     /**
23909      * @cfg {String} dropAllowed
23910      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23911      */
23912     dropAllowed : "x-dd-drop-ok",
23913     /**
23914      * @cfg {String} dropNotAllowed
23915      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23916      */
23917     dropNotAllowed : "x-dd-drop-nodrop",
23918     /**
23919      * @cfg {boolean} success
23920      * set this after drop listener.. 
23921      */
23922     success : false,
23923     /**
23924      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23925      * if the drop point is valid for over/enter..
23926      */
23927     valid : false,
23928     // private
23929     isTarget : true,
23930
23931     // private
23932     isNotifyTarget : true,
23933     
23934     /**
23935      * @hide
23936      */
23937     notifyEnter : function(dd, e, data)
23938     {
23939         this.valid = true;
23940         this.fireEvent('enter', dd, e, data);
23941         if(this.overClass){
23942             this.el.addClass(this.overClass);
23943         }
23944         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23945             this.valid ? this.dropAllowed : this.dropNotAllowed
23946         );
23947     },
23948
23949     /**
23950      * @hide
23951      */
23952     notifyOver : function(dd, e, data)
23953     {
23954         this.valid = true;
23955         this.fireEvent('over', dd, e, data);
23956         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23957             this.valid ? this.dropAllowed : this.dropNotAllowed
23958         );
23959     },
23960
23961     /**
23962      * @hide
23963      */
23964     notifyOut : function(dd, e, data)
23965     {
23966         this.fireEvent('out', dd, e, data);
23967         if(this.overClass){
23968             this.el.removeClass(this.overClass);
23969         }
23970     },
23971
23972     /**
23973      * @hide
23974      */
23975     notifyDrop : function(dd, e, data)
23976     {
23977         this.success = false;
23978         this.fireEvent('drop', dd, e, data);
23979         return this.success;
23980     }
23981 });/*
23982  * Based on:
23983  * Ext JS Library 1.1.1
23984  * Copyright(c) 2006-2007, Ext JS, LLC.
23985  *
23986  * Originally Released Under LGPL - original licence link has changed is not relivant.
23987  *
23988  * Fork - LGPL
23989  * <script type="text/javascript">
23990  */
23991
23992
23993 /**
23994  * @class Roo.dd.DragZone
23995  * @extends Roo.dd.DragSource
23996  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23997  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23998  * @constructor
23999  * @param {String/HTMLElement/Element} el The container element
24000  * @param {Object} config
24001  */
24002 Roo.dd.DragZone = function(el, config){
24003     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24004     if(this.containerScroll){
24005         Roo.dd.ScrollManager.register(this.el);
24006     }
24007 };
24008
24009 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24010     /**
24011      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24012      * for auto scrolling during drag operations.
24013      */
24014     /**
24015      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24016      * method after a failed drop (defaults to "c3daf9" - light blue)
24017      */
24018
24019     /**
24020      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24021      * for a valid target to drag based on the mouse down. Override this method
24022      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24023      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24024      * @param {EventObject} e The mouse down event
24025      * @return {Object} The dragData
24026      */
24027     getDragData : function(e){
24028         return Roo.dd.Registry.getHandleFromEvent(e);
24029     },
24030     
24031     /**
24032      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24033      * this.dragData.ddel
24034      * @param {Number} x The x position of the click on the dragged object
24035      * @param {Number} y The y position of the click on the dragged object
24036      * @return {Boolean} true to continue the drag, false to cancel
24037      */
24038     onInitDrag : function(x, y){
24039         this.proxy.update(this.dragData.ddel.cloneNode(true));
24040         this.onStartDrag(x, y);
24041         return true;
24042     },
24043     
24044     /**
24045      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24046      */
24047     afterRepair : function(){
24048         if(Roo.enableFx){
24049             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24050         }
24051         this.dragging = false;
24052     },
24053
24054     /**
24055      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24056      * the XY of this.dragData.ddel
24057      * @param {EventObject} e The mouse up event
24058      * @return {Array} The xy location (e.g. [100, 200])
24059      */
24060     getRepairXY : function(e){
24061         return Roo.Element.fly(this.dragData.ddel).getXY();  
24062     }
24063 });/*
24064  * Based on:
24065  * Ext JS Library 1.1.1
24066  * Copyright(c) 2006-2007, Ext JS, LLC.
24067  *
24068  * Originally Released Under LGPL - original licence link has changed is not relivant.
24069  *
24070  * Fork - LGPL
24071  * <script type="text/javascript">
24072  */
24073 /**
24074  * @class Roo.dd.DropZone
24075  * @extends Roo.dd.DropTarget
24076  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24077  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24078  * @constructor
24079  * @param {String/HTMLElement/Element} el The container element
24080  * @param {Object} config
24081  */
24082 Roo.dd.DropZone = function(el, config){
24083     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24084 };
24085
24086 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24087     /**
24088      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24089      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24090      * provide your own custom lookup.
24091      * @param {Event} e The event
24092      * @return {Object} data The custom data
24093      */
24094     getTargetFromEvent : function(e){
24095         return Roo.dd.Registry.getTargetFromEvent(e);
24096     },
24097
24098     /**
24099      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24100      * that it has registered.  This method has no default implementation and should be overridden to provide
24101      * node-specific processing if necessary.
24102      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24103      * {@link #getTargetFromEvent} for this node)
24104      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24105      * @param {Event} e The event
24106      * @param {Object} data An object containing arbitrary data supplied by the drag source
24107      */
24108     onNodeEnter : function(n, dd, e, data){
24109         
24110     },
24111
24112     /**
24113      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24114      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24115      * overridden to provide the proper feedback.
24116      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24117      * {@link #getTargetFromEvent} for this node)
24118      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24119      * @param {Event} e The event
24120      * @param {Object} data An object containing arbitrary data supplied by the drag source
24121      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24122      * underlying {@link Roo.dd.StatusProxy} can be updated
24123      */
24124     onNodeOver : function(n, dd, e, data){
24125         return this.dropAllowed;
24126     },
24127
24128     /**
24129      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24130      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24131      * node-specific processing if necessary.
24132      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24133      * {@link #getTargetFromEvent} for this node)
24134      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24135      * @param {Event} e The event
24136      * @param {Object} data An object containing arbitrary data supplied by the drag source
24137      */
24138     onNodeOut : function(n, dd, e, data){
24139         
24140     },
24141
24142     /**
24143      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24144      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24145      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24146      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24147      * {@link #getTargetFromEvent} for this node)
24148      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24149      * @param {Event} e The event
24150      * @param {Object} data An object containing arbitrary data supplied by the drag source
24151      * @return {Boolean} True if the drop was valid, else false
24152      */
24153     onNodeDrop : function(n, dd, e, data){
24154         return false;
24155     },
24156
24157     /**
24158      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24159      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24160      * it should be overridden to provide the proper feedback if necessary.
24161      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24162      * @param {Event} e The event
24163      * @param {Object} data An object containing arbitrary data supplied by the drag source
24164      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24165      * underlying {@link Roo.dd.StatusProxy} can be updated
24166      */
24167     onContainerOver : function(dd, e, data){
24168         return this.dropNotAllowed;
24169     },
24170
24171     /**
24172      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24173      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24174      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24175      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24176      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24177      * @param {Event} e The event
24178      * @param {Object} data An object containing arbitrary data supplied by the drag source
24179      * @return {Boolean} True if the drop was valid, else false
24180      */
24181     onContainerDrop : function(dd, e, data){
24182         return false;
24183     },
24184
24185     /**
24186      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24187      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24188      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24189      * you should override this method and provide a custom implementation.
24190      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24191      * @param {Event} e The event
24192      * @param {Object} data An object containing arbitrary data supplied by the drag source
24193      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24194      * underlying {@link Roo.dd.StatusProxy} can be updated
24195      */
24196     notifyEnter : function(dd, e, data){
24197         return this.dropNotAllowed;
24198     },
24199
24200     /**
24201      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24202      * This method will be called on every mouse movement while the drag source is over the drop zone.
24203      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24204      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24205      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24206      * registered node, it will call {@link #onContainerOver}.
24207      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24208      * @param {Event} e The event
24209      * @param {Object} data An object containing arbitrary data supplied by the drag source
24210      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24211      * underlying {@link Roo.dd.StatusProxy} can be updated
24212      */
24213     notifyOver : function(dd, e, data){
24214         var n = this.getTargetFromEvent(e);
24215         if(!n){ // not over valid drop target
24216             if(this.lastOverNode){
24217                 this.onNodeOut(this.lastOverNode, dd, e, data);
24218                 this.lastOverNode = null;
24219             }
24220             return this.onContainerOver(dd, e, data);
24221         }
24222         if(this.lastOverNode != n){
24223             if(this.lastOverNode){
24224                 this.onNodeOut(this.lastOverNode, dd, e, data);
24225             }
24226             this.onNodeEnter(n, dd, e, data);
24227             this.lastOverNode = n;
24228         }
24229         return this.onNodeOver(n, dd, e, data);
24230     },
24231
24232     /**
24233      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24234      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24235      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24236      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24237      * @param {Event} e The event
24238      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24239      */
24240     notifyOut : function(dd, e, data){
24241         if(this.lastOverNode){
24242             this.onNodeOut(this.lastOverNode, dd, e, data);
24243             this.lastOverNode = null;
24244         }
24245     },
24246
24247     /**
24248      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24249      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24250      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24251      * otherwise it will call {@link #onContainerDrop}.
24252      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24253      * @param {Event} e The event
24254      * @param {Object} data An object containing arbitrary data supplied by the drag source
24255      * @return {Boolean} True if the drop was valid, else false
24256      */
24257     notifyDrop : function(dd, e, data){
24258         if(this.lastOverNode){
24259             this.onNodeOut(this.lastOverNode, dd, e, data);
24260             this.lastOverNode = null;
24261         }
24262         var n = this.getTargetFromEvent(e);
24263         return n ?
24264             this.onNodeDrop(n, dd, e, data) :
24265             this.onContainerDrop(dd, e, data);
24266     },
24267
24268     // private
24269     triggerCacheRefresh : function(){
24270         Roo.dd.DDM.refreshCache(this.groups);
24271     }  
24272 });/*
24273  * Based on:
24274  * Ext JS Library 1.1.1
24275  * Copyright(c) 2006-2007, Ext JS, LLC.
24276  *
24277  * Originally Released Under LGPL - original licence link has changed is not relivant.
24278  *
24279  * Fork - LGPL
24280  * <script type="text/javascript">
24281  */
24282
24283
24284 /**
24285  * @class Roo.data.SortTypes
24286  * @static
24287  * Defines the default sorting (casting?) comparison functions used when sorting data.
24288  */
24289 Roo.data.SortTypes = {
24290     /**
24291      * Default sort that does nothing
24292      * @param {Mixed} s The value being converted
24293      * @return {Mixed} The comparison value
24294      */
24295     none : function(s){
24296         return s;
24297     },
24298     
24299     /**
24300      * The regular expression used to strip tags
24301      * @type {RegExp}
24302      * @property
24303      */
24304     stripTagsRE : /<\/?[^>]+>/gi,
24305     
24306     /**
24307      * Strips all HTML tags to sort on text only
24308      * @param {Mixed} s The value being converted
24309      * @return {String} The comparison value
24310      */
24311     asText : function(s){
24312         return String(s).replace(this.stripTagsRE, "");
24313     },
24314     
24315     /**
24316      * Strips all HTML tags to sort on text only - Case insensitive
24317      * @param {Mixed} s The value being converted
24318      * @return {String} The comparison value
24319      */
24320     asUCText : function(s){
24321         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24322     },
24323     
24324     /**
24325      * Case insensitive string
24326      * @param {Mixed} s The value being converted
24327      * @return {String} The comparison value
24328      */
24329     asUCString : function(s) {
24330         return String(s).toUpperCase();
24331     },
24332     
24333     /**
24334      * Date sorting
24335      * @param {Mixed} s The value being converted
24336      * @return {Number} The comparison value
24337      */
24338     asDate : function(s) {
24339         if(!s){
24340             return 0;
24341         }
24342         if(s instanceof Date){
24343             return s.getTime();
24344         }
24345         return Date.parse(String(s));
24346     },
24347     
24348     /**
24349      * Float sorting
24350      * @param {Mixed} s The value being converted
24351      * @return {Float} The comparison value
24352      */
24353     asFloat : function(s) {
24354         var val = parseFloat(String(s).replace(/,/g, ""));
24355         if(isNaN(val)) {
24356             val = 0;
24357         }
24358         return val;
24359     },
24360     
24361     /**
24362      * Integer sorting
24363      * @param {Mixed} s The value being converted
24364      * @return {Number} The comparison value
24365      */
24366     asInt : function(s) {
24367         var val = parseInt(String(s).replace(/,/g, ""));
24368         if(isNaN(val)) {
24369             val = 0;
24370         }
24371         return val;
24372     }
24373 };/*
24374  * Based on:
24375  * Ext JS Library 1.1.1
24376  * Copyright(c) 2006-2007, Ext JS, LLC.
24377  *
24378  * Originally Released Under LGPL - original licence link has changed is not relivant.
24379  *
24380  * Fork - LGPL
24381  * <script type="text/javascript">
24382  */
24383
24384 /**
24385 * @class Roo.data.Record
24386  * Instances of this class encapsulate both record <em>definition</em> information, and record
24387  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24388  * to access Records cached in an {@link Roo.data.Store} object.<br>
24389  * <p>
24390  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24391  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24392  * objects.<br>
24393  * <p>
24394  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24395  * @constructor
24396  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24397  * {@link #create}. The parameters are the same.
24398  * @param {Array} data An associative Array of data values keyed by the field name.
24399  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24400  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24401  * not specified an integer id is generated.
24402  */
24403 Roo.data.Record = function(data, id){
24404     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24405     this.data = data;
24406 };
24407
24408 /**
24409  * Generate a constructor for a specific record layout.
24410  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24411  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24412  * Each field definition object may contain the following properties: <ul>
24413  * <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,
24414  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24415  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24416  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24417  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24418  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24419  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24420  * this may be omitted.</p></li>
24421  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24422  * <ul><li>auto (Default, implies no conversion)</li>
24423  * <li>string</li>
24424  * <li>int</li>
24425  * <li>float</li>
24426  * <li>boolean</li>
24427  * <li>date</li></ul></p></li>
24428  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24429  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24430  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24431  * by the Reader into an object that will be stored in the Record. It is passed the
24432  * following parameters:<ul>
24433  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24434  * </ul></p></li>
24435  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24436  * </ul>
24437  * <br>usage:<br><pre><code>
24438 var TopicRecord = Roo.data.Record.create(
24439     {name: 'title', mapping: 'topic_title'},
24440     {name: 'author', mapping: 'username'},
24441     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24442     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24443     {name: 'lastPoster', mapping: 'user2'},
24444     {name: 'excerpt', mapping: 'post_text'}
24445 );
24446
24447 var myNewRecord = new TopicRecord({
24448     title: 'Do my job please',
24449     author: 'noobie',
24450     totalPosts: 1,
24451     lastPost: new Date(),
24452     lastPoster: 'Animal',
24453     excerpt: 'No way dude!'
24454 });
24455 myStore.add(myNewRecord);
24456 </code></pre>
24457  * @method create
24458  * @static
24459  */
24460 Roo.data.Record.create = function(o){
24461     var f = function(){
24462         f.superclass.constructor.apply(this, arguments);
24463     };
24464     Roo.extend(f, Roo.data.Record);
24465     var p = f.prototype;
24466     p.fields = new Roo.util.MixedCollection(false, function(field){
24467         return field.name;
24468     });
24469     for(var i = 0, len = o.length; i < len; i++){
24470         p.fields.add(new Roo.data.Field(o[i]));
24471     }
24472     f.getField = function(name){
24473         return p.fields.get(name);  
24474     };
24475     return f;
24476 };
24477
24478 Roo.data.Record.AUTO_ID = 1000;
24479 Roo.data.Record.EDIT = 'edit';
24480 Roo.data.Record.REJECT = 'reject';
24481 Roo.data.Record.COMMIT = 'commit';
24482
24483 Roo.data.Record.prototype = {
24484     /**
24485      * Readonly flag - true if this record has been modified.
24486      * @type Boolean
24487      */
24488     dirty : false,
24489     editing : false,
24490     error: null,
24491     modified: null,
24492
24493     // private
24494     join : function(store){
24495         this.store = store;
24496     },
24497
24498     /**
24499      * Set the named field to the specified value.
24500      * @param {String} name The name of the field to set.
24501      * @param {Object} value The value to set the field to.
24502      */
24503     set : function(name, value){
24504         if(this.data[name] == value){
24505             return;
24506         }
24507         this.dirty = true;
24508         if(!this.modified){
24509             this.modified = {};
24510         }
24511         if(typeof this.modified[name] == 'undefined'){
24512             this.modified[name] = this.data[name];
24513         }
24514         this.data[name] = value;
24515         if(!this.editing && this.store){
24516             this.store.afterEdit(this);
24517         }       
24518     },
24519
24520     /**
24521      * Get the value of the named field.
24522      * @param {String} name The name of the field to get the value of.
24523      * @return {Object} The value of the field.
24524      */
24525     get : function(name){
24526         return this.data[name]; 
24527     },
24528
24529     // private
24530     beginEdit : function(){
24531         this.editing = true;
24532         this.modified = {}; 
24533     },
24534
24535     // private
24536     cancelEdit : function(){
24537         this.editing = false;
24538         delete this.modified;
24539     },
24540
24541     // private
24542     endEdit : function(){
24543         this.editing = false;
24544         if(this.dirty && this.store){
24545             this.store.afterEdit(this);
24546         }
24547     },
24548
24549     /**
24550      * Usually called by the {@link Roo.data.Store} which owns the Record.
24551      * Rejects all changes made to the Record since either creation, or the last commit operation.
24552      * Modified fields are reverted to their original values.
24553      * <p>
24554      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24555      * of reject operations.
24556      */
24557     reject : function(){
24558         var m = this.modified;
24559         for(var n in m){
24560             if(typeof m[n] != "function"){
24561                 this.data[n] = m[n];
24562             }
24563         }
24564         this.dirty = false;
24565         delete this.modified;
24566         this.editing = false;
24567         if(this.store){
24568             this.store.afterReject(this);
24569         }
24570     },
24571
24572     /**
24573      * Usually called by the {@link Roo.data.Store} which owns the Record.
24574      * Commits all changes made to the Record since either creation, or the last commit operation.
24575      * <p>
24576      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24577      * of commit operations.
24578      */
24579     commit : function(){
24580         this.dirty = false;
24581         delete this.modified;
24582         this.editing = false;
24583         if(this.store){
24584             this.store.afterCommit(this);
24585         }
24586     },
24587
24588     // private
24589     hasError : function(){
24590         return this.error != null;
24591     },
24592
24593     // private
24594     clearError : function(){
24595         this.error = null;
24596     },
24597
24598     /**
24599      * Creates a copy of this record.
24600      * @param {String} id (optional) A new record id if you don't want to use this record's id
24601      * @return {Record}
24602      */
24603     copy : function(newId) {
24604         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24605     }
24606 };/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616
24617
24618
24619 /**
24620  * @class Roo.data.Store
24621  * @extends Roo.util.Observable
24622  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24623  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24624  * <p>
24625  * 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
24626  * has no knowledge of the format of the data returned by the Proxy.<br>
24627  * <p>
24628  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24629  * instances from the data object. These records are cached and made available through accessor functions.
24630  * @constructor
24631  * Creates a new Store.
24632  * @param {Object} config A config object containing the objects needed for the Store to access data,
24633  * and read the data into Records.
24634  */
24635 Roo.data.Store = function(config){
24636     this.data = new Roo.util.MixedCollection(false);
24637     this.data.getKey = function(o){
24638         return o.id;
24639     };
24640     this.baseParams = {};
24641     // private
24642     this.paramNames = {
24643         "start" : "start",
24644         "limit" : "limit",
24645         "sort" : "sort",
24646         "dir" : "dir",
24647         "multisort" : "_multisort"
24648     };
24649
24650     if(config && config.data){
24651         this.inlineData = config.data;
24652         delete config.data;
24653     }
24654
24655     Roo.apply(this, config);
24656     
24657     if(this.reader){ // reader passed
24658         this.reader = Roo.factory(this.reader, Roo.data);
24659         this.reader.xmodule = this.xmodule || false;
24660         if(!this.recordType){
24661             this.recordType = this.reader.recordType;
24662         }
24663         if(this.reader.onMetaChange){
24664             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24665         }
24666     }
24667
24668     if(this.recordType){
24669         this.fields = this.recordType.prototype.fields;
24670     }
24671     this.modified = [];
24672
24673     this.addEvents({
24674         /**
24675          * @event datachanged
24676          * Fires when the data cache has changed, and a widget which is using this Store
24677          * as a Record cache should refresh its view.
24678          * @param {Store} this
24679          */
24680         datachanged : true,
24681         /**
24682          * @event metachange
24683          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24684          * @param {Store} this
24685          * @param {Object} meta The JSON metadata
24686          */
24687         metachange : true,
24688         /**
24689          * @event add
24690          * Fires when Records have been added to the Store
24691          * @param {Store} this
24692          * @param {Roo.data.Record[]} records The array of Records added
24693          * @param {Number} index The index at which the record(s) were added
24694          */
24695         add : true,
24696         /**
24697          * @event remove
24698          * Fires when a Record has been removed from the Store
24699          * @param {Store} this
24700          * @param {Roo.data.Record} record The Record that was removed
24701          * @param {Number} index The index at which the record was removed
24702          */
24703         remove : true,
24704         /**
24705          * @event update
24706          * Fires when a Record has been updated
24707          * @param {Store} this
24708          * @param {Roo.data.Record} record The Record that was updated
24709          * @param {String} operation The update operation being performed.  Value may be one of:
24710          * <pre><code>
24711  Roo.data.Record.EDIT
24712  Roo.data.Record.REJECT
24713  Roo.data.Record.COMMIT
24714          * </code></pre>
24715          */
24716         update : true,
24717         /**
24718          * @event clear
24719          * Fires when the data cache has been cleared.
24720          * @param {Store} this
24721          */
24722         clear : true,
24723         /**
24724          * @event beforeload
24725          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24726          * the load action will be canceled.
24727          * @param {Store} this
24728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24729          */
24730         beforeload : true,
24731         /**
24732          * @event beforeloadadd
24733          * Fires after a new set of Records has been loaded.
24734          * @param {Store} this
24735          * @param {Roo.data.Record[]} records The Records that were loaded
24736          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24737          */
24738         beforeloadadd : true,
24739         /**
24740          * @event load
24741          * Fires after a new set of Records has been loaded, before they are added to the store.
24742          * @param {Store} this
24743          * @param {Roo.data.Record[]} records The Records that were loaded
24744          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24745          * @params {Object} return from reader
24746          */
24747         load : true,
24748         /**
24749          * @event loadexception
24750          * Fires if an exception occurs in the Proxy during loading.
24751          * Called with the signature of the Proxy's "loadexception" event.
24752          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24753          * 
24754          * @param {Proxy} 
24755          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24756          * @param {Object} load options 
24757          * @param {Object} jsonData from your request (normally this contains the Exception)
24758          */
24759         loadexception : true
24760     });
24761     
24762     if(this.proxy){
24763         this.proxy = Roo.factory(this.proxy, Roo.data);
24764         this.proxy.xmodule = this.xmodule || false;
24765         this.relayEvents(this.proxy,  ["loadexception"]);
24766     }
24767     this.sortToggle = {};
24768     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24769
24770     Roo.data.Store.superclass.constructor.call(this);
24771
24772     if(this.inlineData){
24773         this.loadData(this.inlineData);
24774         delete this.inlineData;
24775     }
24776 };
24777
24778 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24779      /**
24780     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24781     * without a remote query - used by combo/forms at present.
24782     */
24783     
24784     /**
24785     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24786     */
24787     /**
24788     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24789     */
24790     /**
24791     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24792     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24793     */
24794     /**
24795     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24796     * on any HTTP request
24797     */
24798     /**
24799     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24800     */
24801     /**
24802     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24803     */
24804     multiSort: false,
24805     /**
24806     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24807     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24808     */
24809     remoteSort : false,
24810
24811     /**
24812     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24813      * loaded or when a record is removed. (defaults to false).
24814     */
24815     pruneModifiedRecords : false,
24816
24817     // private
24818     lastOptions : null,
24819
24820     /**
24821      * Add Records to the Store and fires the add event.
24822      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24823      */
24824     add : function(records){
24825         records = [].concat(records);
24826         for(var i = 0, len = records.length; i < len; i++){
24827             records[i].join(this);
24828         }
24829         var index = this.data.length;
24830         this.data.addAll(records);
24831         this.fireEvent("add", this, records, index);
24832     },
24833
24834     /**
24835      * Remove a Record from the Store and fires the remove event.
24836      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24837      */
24838     remove : function(record){
24839         var index = this.data.indexOf(record);
24840         this.data.removeAt(index);
24841  
24842         if(this.pruneModifiedRecords){
24843             this.modified.remove(record);
24844         }
24845         this.fireEvent("remove", this, record, index);
24846     },
24847
24848     /**
24849      * Remove all Records from the Store and fires the clear event.
24850      */
24851     removeAll : function(){
24852         this.data.clear();
24853         if(this.pruneModifiedRecords){
24854             this.modified = [];
24855         }
24856         this.fireEvent("clear", this);
24857     },
24858
24859     /**
24860      * Inserts Records to the Store at the given index and fires the add event.
24861      * @param {Number} index The start index at which to insert the passed Records.
24862      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24863      */
24864     insert : function(index, records){
24865         records = [].concat(records);
24866         for(var i = 0, len = records.length; i < len; i++){
24867             this.data.insert(index, records[i]);
24868             records[i].join(this);
24869         }
24870         this.fireEvent("add", this, records, index);
24871     },
24872
24873     /**
24874      * Get the index within the cache of the passed Record.
24875      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24876      * @return {Number} The index of the passed Record. Returns -1 if not found.
24877      */
24878     indexOf : function(record){
24879         return this.data.indexOf(record);
24880     },
24881
24882     /**
24883      * Get the index within the cache of the Record with the passed id.
24884      * @param {String} id The id of the Record to find.
24885      * @return {Number} The index of the Record. Returns -1 if not found.
24886      */
24887     indexOfId : function(id){
24888         return this.data.indexOfKey(id);
24889     },
24890
24891     /**
24892      * Get the Record with the specified id.
24893      * @param {String} id The id of the Record to find.
24894      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24895      */
24896     getById : function(id){
24897         return this.data.key(id);
24898     },
24899
24900     /**
24901      * Get the Record at the specified index.
24902      * @param {Number} index The index of the Record to find.
24903      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24904      */
24905     getAt : function(index){
24906         return this.data.itemAt(index);
24907     },
24908
24909     /**
24910      * Returns a range of Records between specified indices.
24911      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24912      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24913      * @return {Roo.data.Record[]} An array of Records
24914      */
24915     getRange : function(start, end){
24916         return this.data.getRange(start, end);
24917     },
24918
24919     // private
24920     storeOptions : function(o){
24921         o = Roo.apply({}, o);
24922         delete o.callback;
24923         delete o.scope;
24924         this.lastOptions = o;
24925     },
24926
24927     /**
24928      * Loads the Record cache from the configured Proxy using the configured Reader.
24929      * <p>
24930      * If using remote paging, then the first load call must specify the <em>start</em>
24931      * and <em>limit</em> properties in the options.params property to establish the initial
24932      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24933      * <p>
24934      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24935      * and this call will return before the new data has been loaded. Perform any post-processing
24936      * in a callback function, or in a "load" event handler.</strong>
24937      * <p>
24938      * @param {Object} options An object containing properties which control loading options:<ul>
24939      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24940      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24941      * <pre>
24942                 {
24943                     data : data,  // array of key=>value data like JsonReader
24944                     total : data.length,
24945                     success : true
24946                     
24947                 }
24948         </pre>
24949             }.</li>
24950      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24951      * passed the following arguments:<ul>
24952      * <li>r : Roo.data.Record[]</li>
24953      * <li>options: Options object from the load call</li>
24954      * <li>success: Boolean success indicator</li></ul></li>
24955      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24956      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24957      * </ul>
24958      */
24959     load : function(options){
24960         options = options || {};
24961         if(this.fireEvent("beforeload", this, options) !== false){
24962             this.storeOptions(options);
24963             var p = Roo.apply(options.params || {}, this.baseParams);
24964             // if meta was not loaded from remote source.. try requesting it.
24965             if (!this.reader.metaFromRemote) {
24966                 p._requestMeta = 1;
24967             }
24968             if(this.sortInfo && this.remoteSort){
24969                 var pn = this.paramNames;
24970                 p[pn["sort"]] = this.sortInfo.field;
24971                 p[pn["dir"]] = this.sortInfo.direction;
24972             }
24973             if (this.multiSort) {
24974                 var pn = this.paramNames;
24975                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24976             }
24977             
24978             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24979         }
24980     },
24981
24982     /**
24983      * Reloads the Record cache from the configured Proxy using the configured Reader and
24984      * the options from the last load operation performed.
24985      * @param {Object} options (optional) An object containing properties which may override the options
24986      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24987      * the most recently used options are reused).
24988      */
24989     reload : function(options){
24990         this.load(Roo.applyIf(options||{}, this.lastOptions));
24991     },
24992
24993     // private
24994     // Called as a callback by the Reader during a load operation.
24995     loadRecords : function(o, options, success){
24996          
24997         if(!o){
24998             if(success !== false){
24999                 this.fireEvent("load", this, [], options, o);
25000             }
25001             if(options.callback){
25002                 options.callback.call(options.scope || this, [], options, false);
25003             }
25004             return;
25005         }
25006         // if data returned failure - throw an exception.
25007         if (o.success === false) {
25008             // show a message if no listener is registered.
25009             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25010                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25011             }
25012             // loadmask wil be hooked into this..
25013             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25014             return;
25015         }
25016         var r = o.records, t = o.totalRecords || r.length;
25017         
25018         this.fireEvent("beforeloadadd", this, r, options, o);
25019         
25020         if(!options || options.add !== true){
25021             if(this.pruneModifiedRecords){
25022                 this.modified = [];
25023             }
25024             for(var i = 0, len = r.length; i < len; i++){
25025                 r[i].join(this);
25026             }
25027             if(this.snapshot){
25028                 this.data = this.snapshot;
25029                 delete this.snapshot;
25030             }
25031             this.data.clear();
25032             this.data.addAll(r);
25033             this.totalLength = t;
25034             this.applySort();
25035             this.fireEvent("datachanged", this);
25036         }else{
25037             this.totalLength = Math.max(t, this.data.length+r.length);
25038             this.add(r);
25039         }
25040         
25041         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25042                 
25043             var e = new Roo.data.Record({});
25044
25045             e.set(this.parent.displayField, this.parent.emptyTitle);
25046             e.set(this.parent.valueField, '');
25047
25048             this.insert(0, e);
25049         }
25050             
25051         this.fireEvent("load", this, r, options, o);
25052         if(options.callback){
25053             options.callback.call(options.scope || this, r, options, true);
25054         }
25055     },
25056
25057
25058     /**
25059      * Loads data from a passed data block. A Reader which understands the format of the data
25060      * must have been configured in the constructor.
25061      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25062      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25063      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25064      */
25065     loadData : function(o, append){
25066         var r = this.reader.readRecords(o);
25067         this.loadRecords(r, {add: append}, true);
25068     },
25069     
25070      /**
25071      * using 'cn' the nested child reader read the child array into it's child stores.
25072      * @param {Object} rec The record with a 'children array
25073      */
25074     loadDataFromChildren : function(rec)
25075     {
25076         this.loadData(this.reader.toLoadData(rec));
25077     },
25078     
25079
25080     /**
25081      * Gets the number of cached records.
25082      * <p>
25083      * <em>If using paging, this may not be the total size of the dataset. If the data object
25084      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25085      * the data set size</em>
25086      */
25087     getCount : function(){
25088         return this.data.length || 0;
25089     },
25090
25091     /**
25092      * Gets the total number of records in the dataset as returned by the server.
25093      * <p>
25094      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25095      * the dataset size</em>
25096      */
25097     getTotalCount : function(){
25098         return this.totalLength || 0;
25099     },
25100
25101     /**
25102      * Returns the sort state of the Store as an object with two properties:
25103      * <pre><code>
25104  field {String} The name of the field by which the Records are sorted
25105  direction {String} The sort order, "ASC" or "DESC"
25106      * </code></pre>
25107      */
25108     getSortState : function(){
25109         return this.sortInfo;
25110     },
25111
25112     // private
25113     applySort : function(){
25114         if(this.sortInfo && !this.remoteSort){
25115             var s = this.sortInfo, f = s.field;
25116             var st = this.fields.get(f).sortType;
25117             var fn = function(r1, r2){
25118                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25119                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25120             };
25121             this.data.sort(s.direction, fn);
25122             if(this.snapshot && this.snapshot != this.data){
25123                 this.snapshot.sort(s.direction, fn);
25124             }
25125         }
25126     },
25127
25128     /**
25129      * Sets the default sort column and order to be used by the next load operation.
25130      * @param {String} fieldName The name of the field to sort by.
25131      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25132      */
25133     setDefaultSort : function(field, dir){
25134         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25135     },
25136
25137     /**
25138      * Sort the Records.
25139      * If remote sorting is used, the sort is performed on the server, and the cache is
25140      * reloaded. If local sorting is used, the cache is sorted internally.
25141      * @param {String} fieldName The name of the field to sort by.
25142      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25143      */
25144     sort : function(fieldName, dir){
25145         var f = this.fields.get(fieldName);
25146         if(!dir){
25147             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25148             
25149             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25150                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25151             }else{
25152                 dir = f.sortDir;
25153             }
25154         }
25155         this.sortToggle[f.name] = dir;
25156         this.sortInfo = {field: f.name, direction: dir};
25157         if(!this.remoteSort){
25158             this.applySort();
25159             this.fireEvent("datachanged", this);
25160         }else{
25161             this.load(this.lastOptions);
25162         }
25163     },
25164
25165     /**
25166      * Calls the specified function for each of the Records in the cache.
25167      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25168      * Returning <em>false</em> aborts and exits the iteration.
25169      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25170      */
25171     each : function(fn, scope){
25172         this.data.each(fn, scope);
25173     },
25174
25175     /**
25176      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25177      * (e.g., during paging).
25178      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25179      */
25180     getModifiedRecords : function(){
25181         return this.modified;
25182     },
25183
25184     // private
25185     createFilterFn : function(property, value, anyMatch){
25186         if(!value.exec){ // not a regex
25187             value = String(value);
25188             if(value.length == 0){
25189                 return false;
25190             }
25191             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25192         }
25193         return function(r){
25194             return value.test(r.data[property]);
25195         };
25196     },
25197
25198     /**
25199      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25200      * @param {String} property A field on your records
25201      * @param {Number} start The record index to start at (defaults to 0)
25202      * @param {Number} end The last record index to include (defaults to length - 1)
25203      * @return {Number} The sum
25204      */
25205     sum : function(property, start, end){
25206         var rs = this.data.items, v = 0;
25207         start = start || 0;
25208         end = (end || end === 0) ? end : rs.length-1;
25209
25210         for(var i = start; i <= end; i++){
25211             v += (rs[i].data[property] || 0);
25212         }
25213         return v;
25214     },
25215
25216     /**
25217      * Filter the records by a specified property.
25218      * @param {String} field A field on your records
25219      * @param {String/RegExp} value Either a string that the field
25220      * should start with or a RegExp to test against the field
25221      * @param {Boolean} anyMatch True to match any part not just the beginning
25222      */
25223     filter : function(property, value, anyMatch){
25224         var fn = this.createFilterFn(property, value, anyMatch);
25225         return fn ? this.filterBy(fn) : this.clearFilter();
25226     },
25227
25228     /**
25229      * Filter by a function. The specified function will be called with each
25230      * record in this data source. If the function returns true the record is included,
25231      * otherwise it is filtered.
25232      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25233      * @param {Object} scope (optional) The scope of the function (defaults to this)
25234      */
25235     filterBy : function(fn, scope){
25236         this.snapshot = this.snapshot || this.data;
25237         this.data = this.queryBy(fn, scope||this);
25238         this.fireEvent("datachanged", this);
25239     },
25240
25241     /**
25242      * Query the records by a specified property.
25243      * @param {String} field A field on your records
25244      * @param {String/RegExp} value Either a string that the field
25245      * should start with or a RegExp to test against the field
25246      * @param {Boolean} anyMatch True to match any part not just the beginning
25247      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25248      */
25249     query : function(property, value, anyMatch){
25250         var fn = this.createFilterFn(property, value, anyMatch);
25251         return fn ? this.queryBy(fn) : this.data.clone();
25252     },
25253
25254     /**
25255      * Query by a function. The specified function will be called with each
25256      * record in this data source. If the function returns true the record is included
25257      * in the results.
25258      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25259      * @param {Object} scope (optional) The scope of the function (defaults to this)
25260       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25261      **/
25262     queryBy : function(fn, scope){
25263         var data = this.snapshot || this.data;
25264         return data.filterBy(fn, scope||this);
25265     },
25266
25267     /**
25268      * Collects unique values for a particular dataIndex from this store.
25269      * @param {String} dataIndex The property to collect
25270      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25271      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25272      * @return {Array} An array of the unique values
25273      **/
25274     collect : function(dataIndex, allowNull, bypassFilter){
25275         var d = (bypassFilter === true && this.snapshot) ?
25276                 this.snapshot.items : this.data.items;
25277         var v, sv, r = [], l = {};
25278         for(var i = 0, len = d.length; i < len; i++){
25279             v = d[i].data[dataIndex];
25280             sv = String(v);
25281             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25282                 l[sv] = true;
25283                 r[r.length] = v;
25284             }
25285         }
25286         return r;
25287     },
25288
25289     /**
25290      * Revert to a view of the Record cache with no filtering applied.
25291      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25292      */
25293     clearFilter : function(suppressEvent){
25294         if(this.snapshot && this.snapshot != this.data){
25295             this.data = this.snapshot;
25296             delete this.snapshot;
25297             if(suppressEvent !== true){
25298                 this.fireEvent("datachanged", this);
25299             }
25300         }
25301     },
25302
25303     // private
25304     afterEdit : function(record){
25305         if(this.modified.indexOf(record) == -1){
25306             this.modified.push(record);
25307         }
25308         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25309     },
25310     
25311     // private
25312     afterReject : function(record){
25313         this.modified.remove(record);
25314         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25315     },
25316
25317     // private
25318     afterCommit : function(record){
25319         this.modified.remove(record);
25320         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25321     },
25322
25323     /**
25324      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25325      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25326      */
25327     commitChanges : function(){
25328         var m = this.modified.slice(0);
25329         this.modified = [];
25330         for(var i = 0, len = m.length; i < len; i++){
25331             m[i].commit();
25332         }
25333     },
25334
25335     /**
25336      * Cancel outstanding changes on all changed records.
25337      */
25338     rejectChanges : function(){
25339         var m = this.modified.slice(0);
25340         this.modified = [];
25341         for(var i = 0, len = m.length; i < len; i++){
25342             m[i].reject();
25343         }
25344     },
25345
25346     onMetaChange : function(meta, rtype, o){
25347         this.recordType = rtype;
25348         this.fields = rtype.prototype.fields;
25349         delete this.snapshot;
25350         this.sortInfo = meta.sortInfo || this.sortInfo;
25351         this.modified = [];
25352         this.fireEvent('metachange', this, this.reader.meta);
25353     },
25354     
25355     moveIndex : function(data, type)
25356     {
25357         var index = this.indexOf(data);
25358         
25359         var newIndex = index + type;
25360         
25361         this.remove(data);
25362         
25363         this.insert(newIndex, data);
25364         
25365     }
25366 });/*
25367  * Based on:
25368  * Ext JS Library 1.1.1
25369  * Copyright(c) 2006-2007, Ext JS, LLC.
25370  *
25371  * Originally Released Under LGPL - original licence link has changed is not relivant.
25372  *
25373  * Fork - LGPL
25374  * <script type="text/javascript">
25375  */
25376
25377 /**
25378  * @class Roo.data.SimpleStore
25379  * @extends Roo.data.Store
25380  * Small helper class to make creating Stores from Array data easier.
25381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25382  * @cfg {Array} fields An array of field definition objects, or field name strings.
25383  * @cfg {Object} an existing reader (eg. copied from another store)
25384  * @cfg {Array} data The multi-dimensional array of data
25385  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25386  * @cfg {Roo.data.Reader} reader  [not-required] 
25387  * @constructor
25388  * @param {Object} config
25389  */
25390 Roo.data.SimpleStore = function(config)
25391 {
25392     Roo.data.SimpleStore.superclass.constructor.call(this, {
25393         isLocal : true,
25394         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25395                 id: config.id
25396             },
25397             Roo.data.Record.create(config.fields)
25398         ),
25399         proxy : new Roo.data.MemoryProxy(config.data)
25400     });
25401     this.load();
25402 };
25403 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25404  * Based on:
25405  * Ext JS Library 1.1.1
25406  * Copyright(c) 2006-2007, Ext JS, LLC.
25407  *
25408  * Originally Released Under LGPL - original licence link has changed is not relivant.
25409  *
25410  * Fork - LGPL
25411  * <script type="text/javascript">
25412  */
25413
25414 /**
25415 /**
25416  * @extends Roo.data.Store
25417  * @class Roo.data.JsonStore
25418  * Small helper class to make creating Stores for JSON data easier. <br/>
25419 <pre><code>
25420 var store = new Roo.data.JsonStore({
25421     url: 'get-images.php',
25422     root: 'images',
25423     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25424 });
25425 </code></pre>
25426  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25427  * JsonReader and HttpProxy (unless inline data is provided).</b>
25428  * @cfg {Array} fields An array of field definition objects, or field name strings.
25429  * @constructor
25430  * @param {Object} config
25431  */
25432 Roo.data.JsonStore = function(c){
25433     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25434         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25435         reader: new Roo.data.JsonReader(c, c.fields)
25436     }));
25437 };
25438 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25439  * Based on:
25440  * Ext JS Library 1.1.1
25441  * Copyright(c) 2006-2007, Ext JS, LLC.
25442  *
25443  * Originally Released Under LGPL - original licence link has changed is not relivant.
25444  *
25445  * Fork - LGPL
25446  * <script type="text/javascript">
25447  */
25448
25449  
25450 Roo.data.Field = function(config){
25451     if(typeof config == "string"){
25452         config = {name: config};
25453     }
25454     Roo.apply(this, config);
25455     
25456     if(!this.type){
25457         this.type = "auto";
25458     }
25459     
25460     var st = Roo.data.SortTypes;
25461     // named sortTypes are supported, here we look them up
25462     if(typeof this.sortType == "string"){
25463         this.sortType = st[this.sortType];
25464     }
25465     
25466     // set default sortType for strings and dates
25467     if(!this.sortType){
25468         switch(this.type){
25469             case "string":
25470                 this.sortType = st.asUCString;
25471                 break;
25472             case "date":
25473                 this.sortType = st.asDate;
25474                 break;
25475             default:
25476                 this.sortType = st.none;
25477         }
25478     }
25479
25480     // define once
25481     var stripRe = /[\$,%]/g;
25482
25483     // prebuilt conversion function for this field, instead of
25484     // switching every time we're reading a value
25485     if(!this.convert){
25486         var cv, dateFormat = this.dateFormat;
25487         switch(this.type){
25488             case "":
25489             case "auto":
25490             case undefined:
25491                 cv = function(v){ return v; };
25492                 break;
25493             case "string":
25494                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25495                 break;
25496             case "int":
25497                 cv = function(v){
25498                     return v !== undefined && v !== null && v !== '' ?
25499                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25500                     };
25501                 break;
25502             case "float":
25503                 cv = function(v){
25504                     return v !== undefined && v !== null && v !== '' ?
25505                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25506                     };
25507                 break;
25508             case "bool":
25509             case "boolean":
25510                 cv = function(v){ return v === true || v === "true" || v == 1; };
25511                 break;
25512             case "date":
25513                 cv = function(v){
25514                     if(!v){
25515                         return '';
25516                     }
25517                     if(v instanceof Date){
25518                         return v;
25519                     }
25520                     if(dateFormat){
25521                         if(dateFormat == "timestamp"){
25522                             return new Date(v*1000);
25523                         }
25524                         return Date.parseDate(v, dateFormat);
25525                     }
25526                     var parsed = Date.parse(v);
25527                     return parsed ? new Date(parsed) : null;
25528                 };
25529              break;
25530             
25531         }
25532         this.convert = cv;
25533     }
25534 };
25535
25536 Roo.data.Field.prototype = {
25537     dateFormat: null,
25538     defaultValue: "",
25539     mapping: null,
25540     sortType : null,
25541     sortDir : "ASC"
25542 };/*
25543  * Based on:
25544  * Ext JS Library 1.1.1
25545  * Copyright(c) 2006-2007, Ext JS, LLC.
25546  *
25547  * Originally Released Under LGPL - original licence link has changed is not relivant.
25548  *
25549  * Fork - LGPL
25550  * <script type="text/javascript">
25551  */
25552  
25553 // Base class for reading structured data from a data source.  This class is intended to be
25554 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25555
25556 /**
25557  * @class Roo.data.DataReader
25558  * @abstract
25559  * Base class for reading structured data from a data source.  This class is intended to be
25560  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25561  */
25562
25563 Roo.data.DataReader = function(meta, recordType){
25564     
25565     this.meta = meta;
25566     
25567     this.recordType = recordType instanceof Array ? 
25568         Roo.data.Record.create(recordType) : recordType;
25569 };
25570
25571 Roo.data.DataReader.prototype = {
25572     
25573     
25574     readerType : 'Data',
25575      /**
25576      * Create an empty record
25577      * @param {Object} data (optional) - overlay some values
25578      * @return {Roo.data.Record} record created.
25579      */
25580     newRow :  function(d) {
25581         var da =  {};
25582         this.recordType.prototype.fields.each(function(c) {
25583             switch( c.type) {
25584                 case 'int' : da[c.name] = 0; break;
25585                 case 'date' : da[c.name] = new Date(); break;
25586                 case 'float' : da[c.name] = 0.0; break;
25587                 case 'boolean' : da[c.name] = false; break;
25588                 default : da[c.name] = ""; break;
25589             }
25590             
25591         });
25592         return new this.recordType(Roo.apply(da, d));
25593     }
25594     
25595     
25596 };/*
25597  * Based on:
25598  * Ext JS Library 1.1.1
25599  * Copyright(c) 2006-2007, Ext JS, LLC.
25600  *
25601  * Originally Released Under LGPL - original licence link has changed is not relivant.
25602  *
25603  * Fork - LGPL
25604  * <script type="text/javascript">
25605  */
25606
25607 /**
25608  * @class Roo.data.DataProxy
25609  * @extends Roo.util.Observable
25610  * @abstract
25611  * This class is an abstract base class for implementations which provide retrieval of
25612  * unformatted data objects.<br>
25613  * <p>
25614  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25615  * (of the appropriate type which knows how to parse the data object) to provide a block of
25616  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25617  * <p>
25618  * Custom implementations must implement the load method as described in
25619  * {@link Roo.data.HttpProxy#load}.
25620  */
25621 Roo.data.DataProxy = function(){
25622     this.addEvents({
25623         /**
25624          * @event beforeload
25625          * Fires before a network request is made to retrieve a data object.
25626          * @param {Object} This DataProxy object.
25627          * @param {Object} params The params parameter to the load function.
25628          */
25629         beforeload : true,
25630         /**
25631          * @event load
25632          * Fires before the load method's callback is called.
25633          * @param {Object} This DataProxy object.
25634          * @param {Object} o The data object.
25635          * @param {Object} arg The callback argument object passed to the load function.
25636          */
25637         load : true,
25638         /**
25639          * @event loadexception
25640          * Fires if an Exception occurs during data retrieval.
25641          * @param {Object} This DataProxy object.
25642          * @param {Object} o The data object.
25643          * @param {Object} arg The callback argument object passed to the load function.
25644          * @param {Object} e The Exception.
25645          */
25646         loadexception : true
25647     });
25648     Roo.data.DataProxy.superclass.constructor.call(this);
25649 };
25650
25651 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25652
25653     /**
25654      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25655      */
25656 /*
25657  * Based on:
25658  * Ext JS Library 1.1.1
25659  * Copyright(c) 2006-2007, Ext JS, LLC.
25660  *
25661  * Originally Released Under LGPL - original licence link has changed is not relivant.
25662  *
25663  * Fork - LGPL
25664  * <script type="text/javascript">
25665  */
25666 /**
25667  * @class Roo.data.MemoryProxy
25668  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25669  * to the Reader when its load method is called.
25670  * @constructor
25671  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25672  */
25673 Roo.data.MemoryProxy = function(data){
25674     if (data.data) {
25675         data = data.data;
25676     }
25677     Roo.data.MemoryProxy.superclass.constructor.call(this);
25678     this.data = data;
25679 };
25680
25681 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25682     
25683     /**
25684      * Load data from the requested source (in this case an in-memory
25685      * data object passed to the constructor), read the data object into
25686      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25687      * process that block using the passed callback.
25688      * @param {Object} params This parameter is not used by the MemoryProxy class.
25689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25690      * object into a block of Roo.data.Records.
25691      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25692      * The function must be passed <ul>
25693      * <li>The Record block object</li>
25694      * <li>The "arg" argument from the load function</li>
25695      * <li>A boolean success indicator</li>
25696      * </ul>
25697      * @param {Object} scope The scope in which to call the callback
25698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25699      */
25700     load : function(params, reader, callback, scope, arg){
25701         params = params || {};
25702         var result;
25703         try {
25704             result = reader.readRecords(params.data ? params.data :this.data);
25705         }catch(e){
25706             this.fireEvent("loadexception", this, arg, null, e);
25707             callback.call(scope, null, arg, false);
25708             return;
25709         }
25710         callback.call(scope, result, arg, true);
25711     },
25712     
25713     // private
25714     update : function(params, records){
25715         
25716     }
25717 });/*
25718  * Based on:
25719  * Ext JS Library 1.1.1
25720  * Copyright(c) 2006-2007, Ext JS, LLC.
25721  *
25722  * Originally Released Under LGPL - original licence link has changed is not relivant.
25723  *
25724  * Fork - LGPL
25725  * <script type="text/javascript">
25726  */
25727 /**
25728  * @class Roo.data.HttpProxy
25729  * @extends Roo.data.DataProxy
25730  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25731  * configured to reference a certain URL.<br><br>
25732  * <p>
25733  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25734  * from which the running page was served.<br><br>
25735  * <p>
25736  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25737  * <p>
25738  * Be aware that to enable the browser to parse an XML document, the server must set
25739  * the Content-Type header in the HTTP response to "text/xml".
25740  * @constructor
25741  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25742  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25743  * will be used to make the request.
25744  */
25745 Roo.data.HttpProxy = function(conn){
25746     Roo.data.HttpProxy.superclass.constructor.call(this);
25747     // is conn a conn config or a real conn?
25748     this.conn = conn;
25749     this.useAjax = !conn || !conn.events;
25750   
25751 };
25752
25753 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25754     // thse are take from connection...
25755     
25756     /**
25757      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25758      */
25759     /**
25760      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25761      * extra parameters to each request made by this object. (defaults to undefined)
25762      */
25763     /**
25764      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25765      *  to each request made by this object. (defaults to undefined)
25766      */
25767     /**
25768      * @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)
25769      */
25770     /**
25771      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25772      */
25773      /**
25774      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25775      * @type Boolean
25776      */
25777   
25778
25779     /**
25780      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25781      * @type Boolean
25782      */
25783     /**
25784      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25785      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25786      * a finer-grained basis than the DataProxy events.
25787      */
25788     getConnection : function(){
25789         return this.useAjax ? Roo.Ajax : this.conn;
25790     },
25791
25792     /**
25793      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25794      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25795      * process that block using the passed callback.
25796      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25797      * for the request to the remote server.
25798      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25799      * object into a block of Roo.data.Records.
25800      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25801      * The function must be passed <ul>
25802      * <li>The Record block object</li>
25803      * <li>The "arg" argument from the load function</li>
25804      * <li>A boolean success indicator</li>
25805      * </ul>
25806      * @param {Object} scope The scope in which to call the callback
25807      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25808      */
25809     load : function(params, reader, callback, scope, arg){
25810         if(this.fireEvent("beforeload", this, params) !== false){
25811             var  o = {
25812                 params : params || {},
25813                 request: {
25814                     callback : callback,
25815                     scope : scope,
25816                     arg : arg
25817                 },
25818                 reader: reader,
25819                 callback : this.loadResponse,
25820                 scope: this
25821             };
25822             if(this.useAjax){
25823                 Roo.applyIf(o, this.conn);
25824                 if(this.activeRequest){
25825                     Roo.Ajax.abort(this.activeRequest);
25826                 }
25827                 this.activeRequest = Roo.Ajax.request(o);
25828             }else{
25829                 this.conn.request(o);
25830             }
25831         }else{
25832             callback.call(scope||this, null, arg, false);
25833         }
25834     },
25835
25836     // private
25837     loadResponse : function(o, success, response){
25838         delete this.activeRequest;
25839         if(!success){
25840             this.fireEvent("loadexception", this, o, response);
25841             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25842             return;
25843         }
25844         var result;
25845         try {
25846             result = o.reader.read(response);
25847         }catch(e){
25848             o.success = false;
25849             o.raw = { errorMsg : response.responseText };
25850             this.fireEvent("loadexception", this, o, response, e);
25851             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25852             return;
25853         }
25854         
25855         this.fireEvent("load", this, o, o.request.arg);
25856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25857     },
25858
25859     // private
25860     update : function(dataSet){
25861
25862     },
25863
25864     // private
25865     updateResponse : function(dataSet){
25866
25867     }
25868 });/*
25869  * Based on:
25870  * Ext JS Library 1.1.1
25871  * Copyright(c) 2006-2007, Ext JS, LLC.
25872  *
25873  * Originally Released Under LGPL - original licence link has changed is not relivant.
25874  *
25875  * Fork - LGPL
25876  * <script type="text/javascript">
25877  */
25878
25879 /**
25880  * @class Roo.data.ScriptTagProxy
25881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25882  * other than the originating domain of the running page.<br><br>
25883  * <p>
25884  * <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
25885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25886  * <p>
25887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25888  * source code that is used as the source inside a &lt;script> tag.<br><br>
25889  * <p>
25890  * In order for the browser to process the returned data, the server must wrap the data object
25891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25893  * depending on whether the callback name was passed:
25894  * <p>
25895  * <pre><code>
25896 boolean scriptTag = false;
25897 String cb = request.getParameter("callback");
25898 if (cb != null) {
25899     scriptTag = true;
25900     response.setContentType("text/javascript");
25901 } else {
25902     response.setContentType("application/x-json");
25903 }
25904 Writer out = response.getWriter();
25905 if (scriptTag) {
25906     out.write(cb + "(");
25907 }
25908 out.print(dataBlock.toJsonString());
25909 if (scriptTag) {
25910     out.write(");");
25911 }
25912 </pre></code>
25913  *
25914  * @constructor
25915  * @param {Object} config A configuration object.
25916  */
25917 Roo.data.ScriptTagProxy = function(config){
25918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25919     Roo.apply(this, config);
25920     this.head = document.getElementsByTagName("head")[0];
25921 };
25922
25923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25924
25925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25926     /**
25927      * @cfg {String} url The URL from which to request the data object.
25928      */
25929     /**
25930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25931      */
25932     timeout : 30000,
25933     /**
25934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25935      * the server the name of the callback function set up by the load call to process the returned data object.
25936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25937      * javascript output which calls this named function passing the data object as its only parameter.
25938      */
25939     callbackParam : "callback",
25940     /**
25941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25942      * name to the request.
25943      */
25944     nocache : true,
25945
25946     /**
25947      * Load data from the configured URL, read the data object into
25948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25949      * process that block using the passed callback.
25950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25951      * for the request to the remote server.
25952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25953      * object into a block of Roo.data.Records.
25954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25955      * The function must be passed <ul>
25956      * <li>The Record block object</li>
25957      * <li>The "arg" argument from the load function</li>
25958      * <li>A boolean success indicator</li>
25959      * </ul>
25960      * @param {Object} scope The scope in which to call the callback
25961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25962      */
25963     load : function(params, reader, callback, scope, arg){
25964         if(this.fireEvent("beforeload", this, params) !== false){
25965
25966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25967
25968             var url = this.url;
25969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25970             if(this.nocache){
25971                 url += "&_dc=" + (new Date().getTime());
25972             }
25973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25974             var trans = {
25975                 id : transId,
25976                 cb : "stcCallback"+transId,
25977                 scriptId : "stcScript"+transId,
25978                 params : params,
25979                 arg : arg,
25980                 url : url,
25981                 callback : callback,
25982                 scope : scope,
25983                 reader : reader
25984             };
25985             var conn = this;
25986
25987             window[trans.cb] = function(o){
25988                 conn.handleResponse(o, trans);
25989             };
25990
25991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25992
25993             if(this.autoAbort !== false){
25994                 this.abort();
25995             }
25996
25997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25998
25999             var script = document.createElement("script");
26000             script.setAttribute("src", url);
26001             script.setAttribute("type", "text/javascript");
26002             script.setAttribute("id", trans.scriptId);
26003             this.head.appendChild(script);
26004
26005             this.trans = trans;
26006         }else{
26007             callback.call(scope||this, null, arg, false);
26008         }
26009     },
26010
26011     // private
26012     isLoading : function(){
26013         return this.trans ? true : false;
26014     },
26015
26016     /**
26017      * Abort the current server request.
26018      */
26019     abort : function(){
26020         if(this.isLoading()){
26021             this.destroyTrans(this.trans);
26022         }
26023     },
26024
26025     // private
26026     destroyTrans : function(trans, isLoaded){
26027         this.head.removeChild(document.getElementById(trans.scriptId));
26028         clearTimeout(trans.timeoutId);
26029         if(isLoaded){
26030             window[trans.cb] = undefined;
26031             try{
26032                 delete window[trans.cb];
26033             }catch(e){}
26034         }else{
26035             // if hasn't been loaded, wait for load to remove it to prevent script error
26036             window[trans.cb] = function(){
26037                 window[trans.cb] = undefined;
26038                 try{
26039                     delete window[trans.cb];
26040                 }catch(e){}
26041             };
26042         }
26043     },
26044
26045     // private
26046     handleResponse : function(o, trans){
26047         this.trans = false;
26048         this.destroyTrans(trans, true);
26049         var result;
26050         try {
26051             result = trans.reader.readRecords(o);
26052         }catch(e){
26053             this.fireEvent("loadexception", this, o, trans.arg, e);
26054             trans.callback.call(trans.scope||window, null, trans.arg, false);
26055             return;
26056         }
26057         this.fireEvent("load", this, o, trans.arg);
26058         trans.callback.call(trans.scope||window, result, trans.arg, true);
26059     },
26060
26061     // private
26062     handleFailure : function(trans){
26063         this.trans = false;
26064         this.destroyTrans(trans, false);
26065         this.fireEvent("loadexception", this, null, trans.arg);
26066         trans.callback.call(trans.scope||window, null, trans.arg, false);
26067     }
26068 });/*
26069  * Based on:
26070  * Ext JS Library 1.1.1
26071  * Copyright(c) 2006-2007, Ext JS, LLC.
26072  *
26073  * Originally Released Under LGPL - original licence link has changed is not relivant.
26074  *
26075  * Fork - LGPL
26076  * <script type="text/javascript">
26077  */
26078
26079 /**
26080  * @class Roo.data.JsonReader
26081  * @extends Roo.data.DataReader
26082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26083  * based on mappings in a provided Roo.data.Record constructor.
26084  * 
26085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26086  * in the reply previously. 
26087  * 
26088  * <p>
26089  * Example code:
26090  * <pre><code>
26091 var RecordDef = Roo.data.Record.create([
26092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26094 ]);
26095 var myReader = new Roo.data.JsonReader({
26096     totalProperty: "results",    // The property which contains the total dataset size (optional)
26097     root: "rows",                // The property which contains an Array of row objects
26098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26099 }, RecordDef);
26100 </code></pre>
26101  * <p>
26102  * This would consume a JSON file like this:
26103  * <pre><code>
26104 { 'results': 2, 'rows': [
26105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26107 }
26108 </code></pre>
26109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26111  * paged from the remote server.
26112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26113  * @cfg {String} root name of the property which contains the Array of row objects.
26114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26115  * @cfg {Array} fields Array of field definition objects
26116  * @constructor
26117  * Create a new JsonReader
26118  * @param {Object} meta Metadata configuration options
26119  * @param {Object} recordType Either an Array of field definition objects,
26120  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26121  */
26122 Roo.data.JsonReader = function(meta, recordType){
26123     
26124     meta = meta || {};
26125     // set some defaults:
26126     Roo.applyIf(meta, {
26127         totalProperty: 'total',
26128         successProperty : 'success',
26129         root : 'data',
26130         id : 'id'
26131     });
26132     
26133     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26134 };
26135 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26136     
26137     readerType : 'Json',
26138     
26139     /**
26140      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26141      * Used by Store query builder to append _requestMeta to params.
26142      * 
26143      */
26144     metaFromRemote : false,
26145     /**
26146      * This method is only used by a DataProxy which has retrieved data from a remote server.
26147      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26148      * @return {Object} data A data block which is used by an Roo.data.Store object as
26149      * a cache of Roo.data.Records.
26150      */
26151     read : function(response){
26152         var json = response.responseText;
26153        
26154         var o = /* eval:var:o */ eval("("+json+")");
26155         if(!o) {
26156             throw {message: "JsonReader.read: Json object not found"};
26157         }
26158         
26159         if(o.metaData){
26160             
26161             delete this.ef;
26162             this.metaFromRemote = true;
26163             this.meta = o.metaData;
26164             this.recordType = Roo.data.Record.create(o.metaData.fields);
26165             this.onMetaChange(this.meta, this.recordType, o);
26166         }
26167         return this.readRecords(o);
26168     },
26169
26170     // private function a store will implement
26171     onMetaChange : function(meta, recordType, o){
26172
26173     },
26174
26175     /**
26176          * @ignore
26177          */
26178     simpleAccess: function(obj, subsc) {
26179         return obj[subsc];
26180     },
26181
26182         /**
26183          * @ignore
26184          */
26185     getJsonAccessor: function(){
26186         var re = /[\[\.]/;
26187         return function(expr) {
26188             try {
26189                 return(re.test(expr))
26190                     ? new Function("obj", "return obj." + expr)
26191                     : function(obj){
26192                         return obj[expr];
26193                     };
26194             } catch(e){}
26195             return Roo.emptyFn;
26196         };
26197     }(),
26198
26199     /**
26200      * Create a data block containing Roo.data.Records from an XML document.
26201      * @param {Object} o An object which contains an Array of row objects in the property specified
26202      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26203      * which contains the total size of the dataset.
26204      * @return {Object} data A data block which is used by an Roo.data.Store object as
26205      * a cache of Roo.data.Records.
26206      */
26207     readRecords : function(o){
26208         /**
26209          * After any data loads, the raw JSON data is available for further custom processing.
26210          * @type Object
26211          */
26212         this.o = o;
26213         var s = this.meta, Record = this.recordType,
26214             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26215
26216 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26217         if (!this.ef) {
26218             if(s.totalProperty) {
26219                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26220                 }
26221                 if(s.successProperty) {
26222                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26223                 }
26224                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26225                 if (s.id) {
26226                         var g = this.getJsonAccessor(s.id);
26227                         this.getId = function(rec) {
26228                                 var r = g(rec);  
26229                                 return (r === undefined || r === "") ? null : r;
26230                         };
26231                 } else {
26232                         this.getId = function(){return null;};
26233                 }
26234             this.ef = [];
26235             for(var jj = 0; jj < fl; jj++){
26236                 f = fi[jj];
26237                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26238                 this.ef[jj] = this.getJsonAccessor(map);
26239             }
26240         }
26241
26242         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26243         if(s.totalProperty){
26244             var vt = parseInt(this.getTotal(o), 10);
26245             if(!isNaN(vt)){
26246                 totalRecords = vt;
26247             }
26248         }
26249         if(s.successProperty){
26250             var vs = this.getSuccess(o);
26251             if(vs === false || vs === 'false'){
26252                 success = false;
26253             }
26254         }
26255         var records = [];
26256         for(var i = 0; i < c; i++){
26257             var n = root[i];
26258             var values = {};
26259             var id = this.getId(n);
26260             for(var j = 0; j < fl; j++){
26261                 f = fi[j];
26262                                 var v = this.ef[j](n);
26263                                 if (!f.convert) {
26264                                         Roo.log('missing convert for ' + f.name);
26265                                         Roo.log(f);
26266                                         continue;
26267                                 }
26268                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26269             }
26270                         if (!Record) {
26271                                 return {
26272                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26273                                         success : false,
26274                                         records : [],
26275                                         totalRecords : 0
26276                                 };
26277                         }
26278             var record = new Record(values, id);
26279             record.json = n;
26280             records[i] = record;
26281         }
26282         return {
26283             raw : o,
26284             success : success,
26285             records : records,
26286             totalRecords : totalRecords
26287         };
26288     },
26289     // used when loading children.. @see loadDataFromChildren
26290     toLoadData: function(rec)
26291     {
26292         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26293         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26294         return { data : data, total : data.length };
26295         
26296     }
26297 });/*
26298  * Based on:
26299  * Ext JS Library 1.1.1
26300  * Copyright(c) 2006-2007, Ext JS, LLC.
26301  *
26302  * Originally Released Under LGPL - original licence link has changed is not relivant.
26303  *
26304  * Fork - LGPL
26305  * <script type="text/javascript">
26306  */
26307
26308 /**
26309  * @class Roo.data.XmlReader
26310  * @extends Roo.data.DataReader
26311  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26312  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26313  * <p>
26314  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26315  * header in the HTTP response must be set to "text/xml".</em>
26316  * <p>
26317  * Example code:
26318  * <pre><code>
26319 var RecordDef = Roo.data.Record.create([
26320    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26321    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26322 ]);
26323 var myReader = new Roo.data.XmlReader({
26324    totalRecords: "results", // The element which contains the total dataset size (optional)
26325    record: "row",           // The repeated element which contains row information
26326    id: "id"                 // The element within the row that provides an ID for the record (optional)
26327 }, RecordDef);
26328 </code></pre>
26329  * <p>
26330  * This would consume an XML file like this:
26331  * <pre><code>
26332 &lt;?xml?>
26333 &lt;dataset>
26334  &lt;results>2&lt;/results>
26335  &lt;row>
26336    &lt;id>1&lt;/id>
26337    &lt;name>Bill&lt;/name>
26338    &lt;occupation>Gardener&lt;/occupation>
26339  &lt;/row>
26340  &lt;row>
26341    &lt;id>2&lt;/id>
26342    &lt;name>Ben&lt;/name>
26343    &lt;occupation>Horticulturalist&lt;/occupation>
26344  &lt;/row>
26345 &lt;/dataset>
26346 </code></pre>
26347  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26348  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26349  * paged from the remote server.
26350  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26351  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26352  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26353  * a record identifier value.
26354  * @constructor
26355  * Create a new XmlReader
26356  * @param {Object} meta Metadata configuration options
26357  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26358  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26359  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26360  */
26361 Roo.data.XmlReader = function(meta, recordType){
26362     meta = meta || {};
26363     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26364 };
26365 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26366     
26367     readerType : 'Xml',
26368     
26369     /**
26370      * This method is only used by a DataProxy which has retrieved data from a remote server.
26371          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26372          * to contain a method called 'responseXML' that returns an XML document object.
26373      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26374      * a cache of Roo.data.Records.
26375      */
26376     read : function(response){
26377         var doc = response.responseXML;
26378         if(!doc) {
26379             throw {message: "XmlReader.read: XML Document not available"};
26380         }
26381         return this.readRecords(doc);
26382     },
26383
26384     /**
26385      * Create a data block containing Roo.data.Records from an XML document.
26386          * @param {Object} doc A parsed XML document.
26387      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26388      * a cache of Roo.data.Records.
26389      */
26390     readRecords : function(doc){
26391         /**
26392          * After any data loads/reads, the raw XML Document is available for further custom processing.
26393          * @type XMLDocument
26394          */
26395         this.xmlData = doc;
26396         var root = doc.documentElement || doc;
26397         var q = Roo.DomQuery;
26398         var recordType = this.recordType, fields = recordType.prototype.fields;
26399         var sid = this.meta.id;
26400         var totalRecords = 0, success = true;
26401         if(this.meta.totalRecords){
26402             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26403         }
26404         
26405         if(this.meta.success){
26406             var sv = q.selectValue(this.meta.success, root, true);
26407             success = sv !== false && sv !== 'false';
26408         }
26409         var records = [];
26410         var ns = q.select(this.meta.record, root);
26411         for(var i = 0, len = ns.length; i < len; i++) {
26412                 var n = ns[i];
26413                 var values = {};
26414                 var id = sid ? q.selectValue(sid, n) : undefined;
26415                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26416                     var f = fields.items[j];
26417                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26418                     v = f.convert(v);
26419                     values[f.name] = v;
26420                 }
26421                 var record = new recordType(values, id);
26422                 record.node = n;
26423                 records[records.length] = record;
26424             }
26425
26426             return {
26427                 success : success,
26428                 records : records,
26429                 totalRecords : totalRecords || records.length
26430             };
26431     }
26432 });/*
26433  * Based on:
26434  * Ext JS Library 1.1.1
26435  * Copyright(c) 2006-2007, Ext JS, LLC.
26436  *
26437  * Originally Released Under LGPL - original licence link has changed is not relivant.
26438  *
26439  * Fork - LGPL
26440  * <script type="text/javascript">
26441  */
26442
26443 /**
26444  * @class Roo.data.ArrayReader
26445  * @extends Roo.data.DataReader
26446  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26447  * Each element of that Array represents a row of data fields. The
26448  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26449  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26450  * <p>
26451  * Example code:.
26452  * <pre><code>
26453 var RecordDef = Roo.data.Record.create([
26454     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26455     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26456 ]);
26457 var myReader = new Roo.data.ArrayReader({
26458     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26459 }, RecordDef);
26460 </code></pre>
26461  * <p>
26462  * This would consume an Array like this:
26463  * <pre><code>
26464 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26465   </code></pre>
26466  
26467  * @constructor
26468  * Create a new JsonReader
26469  * @param {Object} meta Metadata configuration options.
26470  * @param {Object|Array} recordType Either an Array of field definition objects
26471  * 
26472  * @cfg {Array} fields Array of field definition objects
26473  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26474  * as specified to {@link Roo.data.Record#create},
26475  * or an {@link Roo.data.Record} object
26476  *
26477  * 
26478  * created using {@link Roo.data.Record#create}.
26479  */
26480 Roo.data.ArrayReader = function(meta, recordType)
26481 {    
26482     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26483 };
26484
26485 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26486     
26487       /**
26488      * Create a data block containing Roo.data.Records from an XML document.
26489      * @param {Object} o An Array of row objects which represents the dataset.
26490      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26491      * a cache of Roo.data.Records.
26492      */
26493     readRecords : function(o)
26494     {
26495         var sid = this.meta ? this.meta.id : null;
26496         var recordType = this.recordType, fields = recordType.prototype.fields;
26497         var records = [];
26498         var root = o;
26499         for(var i = 0; i < root.length; i++){
26500             var n = root[i];
26501             var values = {};
26502             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26503             for(var j = 0, jlen = fields.length; j < jlen; j++){
26504                 var f = fields.items[j];
26505                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26506                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26507                 v = f.convert(v);
26508                 values[f.name] = v;
26509             }
26510             var record = new recordType(values, id);
26511             record.json = n;
26512             records[records.length] = record;
26513         }
26514         return {
26515             records : records,
26516             totalRecords : records.length
26517         };
26518     },
26519     // used when loading children.. @see loadDataFromChildren
26520     toLoadData: function(rec)
26521     {
26522         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26523         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26524         
26525     }
26526     
26527     
26528 });/*
26529  * Based on:
26530  * Ext JS Library 1.1.1
26531  * Copyright(c) 2006-2007, Ext JS, LLC.
26532  *
26533  * Originally Released Under LGPL - original licence link has changed is not relivant.
26534  *
26535  * Fork - LGPL
26536  * <script type="text/javascript">
26537  */
26538
26539
26540 /**
26541  * @class Roo.data.Tree
26542  * @extends Roo.util.Observable
26543  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26544  * in the tree have most standard DOM functionality.
26545  * @constructor
26546  * @param {Node} root (optional) The root node
26547  */
26548 Roo.data.Tree = function(root){
26549    this.nodeHash = {};
26550    /**
26551     * The root node for this tree
26552     * @type Node
26553     */
26554    this.root = null;
26555    if(root){
26556        this.setRootNode(root);
26557    }
26558    this.addEvents({
26559        /**
26560         * @event append
26561         * Fires when a new child node is appended to a node in this tree.
26562         * @param {Tree} tree The owner tree
26563         * @param {Node} parent The parent node
26564         * @param {Node} node The newly appended node
26565         * @param {Number} index The index of the newly appended node
26566         */
26567        "append" : true,
26568        /**
26569         * @event remove
26570         * Fires when a child node is removed from a node in this tree.
26571         * @param {Tree} tree The owner tree
26572         * @param {Node} parent The parent node
26573         * @param {Node} node The child node removed
26574         */
26575        "remove" : true,
26576        /**
26577         * @event move
26578         * Fires when a node is moved to a new location in the tree
26579         * @param {Tree} tree The owner tree
26580         * @param {Node} node The node moved
26581         * @param {Node} oldParent The old parent of this node
26582         * @param {Node} newParent The new parent of this node
26583         * @param {Number} index The index it was moved to
26584         */
26585        "move" : true,
26586        /**
26587         * @event insert
26588         * Fires when a new child node is inserted in a node in this tree.
26589         * @param {Tree} tree The owner tree
26590         * @param {Node} parent The parent node
26591         * @param {Node} node The child node inserted
26592         * @param {Node} refNode The child node the node was inserted before
26593         */
26594        "insert" : true,
26595        /**
26596         * @event beforeappend
26597         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26598         * @param {Tree} tree The owner tree
26599         * @param {Node} parent The parent node
26600         * @param {Node} node The child node to be appended
26601         */
26602        "beforeappend" : true,
26603        /**
26604         * @event beforeremove
26605         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26606         * @param {Tree} tree The owner tree
26607         * @param {Node} parent The parent node
26608         * @param {Node} node The child node to be removed
26609         */
26610        "beforeremove" : true,
26611        /**
26612         * @event beforemove
26613         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26614         * @param {Tree} tree The owner tree
26615         * @param {Node} node The node being moved
26616         * @param {Node} oldParent The parent of the node
26617         * @param {Node} newParent The new parent the node is moving to
26618         * @param {Number} index The index it is being moved to
26619         */
26620        "beforemove" : true,
26621        /**
26622         * @event beforeinsert
26623         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26624         * @param {Tree} tree The owner tree
26625         * @param {Node} parent The parent node
26626         * @param {Node} node The child node to be inserted
26627         * @param {Node} refNode The child node the node is being inserted before
26628         */
26629        "beforeinsert" : true
26630    });
26631
26632     Roo.data.Tree.superclass.constructor.call(this);
26633 };
26634
26635 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26636     pathSeparator: "/",
26637
26638     proxyNodeEvent : function(){
26639         return this.fireEvent.apply(this, arguments);
26640     },
26641
26642     /**
26643      * Returns the root node for this tree.
26644      * @return {Node}
26645      */
26646     getRootNode : function(){
26647         return this.root;
26648     },
26649
26650     /**
26651      * Sets the root node for this tree.
26652      * @param {Node} node
26653      * @return {Node}
26654      */
26655     setRootNode : function(node){
26656         this.root = node;
26657         node.ownerTree = this;
26658         node.isRoot = true;
26659         this.registerNode(node);
26660         return node;
26661     },
26662
26663     /**
26664      * Gets a node in this tree by its id.
26665      * @param {String} id
26666      * @return {Node}
26667      */
26668     getNodeById : function(id){
26669         return this.nodeHash[id];
26670     },
26671
26672     registerNode : function(node){
26673         this.nodeHash[node.id] = node;
26674     },
26675
26676     unregisterNode : function(node){
26677         delete this.nodeHash[node.id];
26678     },
26679
26680     toString : function(){
26681         return "[Tree"+(this.id?" "+this.id:"")+"]";
26682     }
26683 });
26684
26685 /**
26686  * @class Roo.data.Node
26687  * @extends Roo.util.Observable
26688  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26689  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26690  * @constructor
26691  * @param {Object} attributes The attributes/config for the node
26692  */
26693 Roo.data.Node = function(attributes){
26694     /**
26695      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26696      * @type {Object}
26697      */
26698     this.attributes = attributes || {};
26699     this.leaf = this.attributes.leaf;
26700     /**
26701      * The node id. @type String
26702      */
26703     this.id = this.attributes.id;
26704     if(!this.id){
26705         this.id = Roo.id(null, "ynode-");
26706         this.attributes.id = this.id;
26707     }
26708      
26709     
26710     /**
26711      * All child nodes of this node. @type Array
26712      */
26713     this.childNodes = [];
26714     if(!this.childNodes.indexOf){ // indexOf is a must
26715         this.childNodes.indexOf = function(o){
26716             for(var i = 0, len = this.length; i < len; i++){
26717                 if(this[i] == o) {
26718                     return i;
26719                 }
26720             }
26721             return -1;
26722         };
26723     }
26724     /**
26725      * The parent node for this node. @type Node
26726      */
26727     this.parentNode = null;
26728     /**
26729      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26730      */
26731     this.firstChild = null;
26732     /**
26733      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26734      */
26735     this.lastChild = null;
26736     /**
26737      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26738      */
26739     this.previousSibling = null;
26740     /**
26741      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26742      */
26743     this.nextSibling = null;
26744
26745     this.addEvents({
26746        /**
26747         * @event append
26748         * Fires when a new child node is appended
26749         * @param {Tree} tree The owner tree
26750         * @param {Node} this This node
26751         * @param {Node} node The newly appended node
26752         * @param {Number} index The index of the newly appended node
26753         */
26754        "append" : true,
26755        /**
26756         * @event remove
26757         * Fires when a child node is removed
26758         * @param {Tree} tree The owner tree
26759         * @param {Node} this This node
26760         * @param {Node} node The removed node
26761         */
26762        "remove" : true,
26763        /**
26764         * @event move
26765         * Fires when this node is moved to a new location in the tree
26766         * @param {Tree} tree The owner tree
26767         * @param {Node} this This node
26768         * @param {Node} oldParent The old parent of this node
26769         * @param {Node} newParent The new parent of this node
26770         * @param {Number} index The index it was moved to
26771         */
26772        "move" : true,
26773        /**
26774         * @event insert
26775         * Fires when a new child node is inserted.
26776         * @param {Tree} tree The owner tree
26777         * @param {Node} this This node
26778         * @param {Node} node The child node inserted
26779         * @param {Node} refNode The child node the node was inserted before
26780         */
26781        "insert" : true,
26782        /**
26783         * @event beforeappend
26784         * Fires before a new child is appended, return false to cancel the append.
26785         * @param {Tree} tree The owner tree
26786         * @param {Node} this This node
26787         * @param {Node} node The child node to be appended
26788         */
26789        "beforeappend" : true,
26790        /**
26791         * @event beforeremove
26792         * Fires before a child is removed, return false to cancel the remove.
26793         * @param {Tree} tree The owner tree
26794         * @param {Node} this This node
26795         * @param {Node} node The child node to be removed
26796         */
26797        "beforeremove" : true,
26798        /**
26799         * @event beforemove
26800         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26801         * @param {Tree} tree The owner tree
26802         * @param {Node} this This node
26803         * @param {Node} oldParent The parent of this node
26804         * @param {Node} newParent The new parent this node is moving to
26805         * @param {Number} index The index it is being moved to
26806         */
26807        "beforemove" : true,
26808        /**
26809         * @event beforeinsert
26810         * Fires before a new child is inserted, return false to cancel the insert.
26811         * @param {Tree} tree The owner tree
26812         * @param {Node} this This node
26813         * @param {Node} node The child node to be inserted
26814         * @param {Node} refNode The child node the node is being inserted before
26815         */
26816        "beforeinsert" : true
26817    });
26818     this.listeners = this.attributes.listeners;
26819     Roo.data.Node.superclass.constructor.call(this);
26820 };
26821
26822 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26823     fireEvent : function(evtName){
26824         // first do standard event for this node
26825         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26826             return false;
26827         }
26828         // then bubble it up to the tree if the event wasn't cancelled
26829         var ot = this.getOwnerTree();
26830         if(ot){
26831             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26832                 return false;
26833             }
26834         }
26835         return true;
26836     },
26837
26838     /**
26839      * Returns true if this node is a leaf
26840      * @return {Boolean}
26841      */
26842     isLeaf : function(){
26843         return this.leaf === true;
26844     },
26845
26846     // private
26847     setFirstChild : function(node){
26848         this.firstChild = node;
26849     },
26850
26851     //private
26852     setLastChild : function(node){
26853         this.lastChild = node;
26854     },
26855
26856
26857     /**
26858      * Returns true if this node is the last child of its parent
26859      * @return {Boolean}
26860      */
26861     isLast : function(){
26862        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26863     },
26864
26865     /**
26866      * Returns true if this node is the first child of its parent
26867      * @return {Boolean}
26868      */
26869     isFirst : function(){
26870        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26871     },
26872
26873     hasChildNodes : function(){
26874         return !this.isLeaf() && this.childNodes.length > 0;
26875     },
26876
26877     /**
26878      * Insert node(s) as the last child node of this node.
26879      * @param {Node/Array} node The node or Array of nodes to append
26880      * @return {Node} The appended node if single append, or null if an array was passed
26881      */
26882     appendChild : function(node){
26883         var multi = false;
26884         if(node instanceof Array){
26885             multi = node;
26886         }else if(arguments.length > 1){
26887             multi = arguments;
26888         }
26889         
26890         // if passed an array or multiple args do them one by one
26891         if(multi){
26892             for(var i = 0, len = multi.length; i < len; i++) {
26893                 this.appendChild(multi[i]);
26894             }
26895         }else{
26896             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26897                 return false;
26898             }
26899             var index = this.childNodes.length;
26900             var oldParent = node.parentNode;
26901             // it's a move, make sure we move it cleanly
26902             if(oldParent){
26903                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26904                     return false;
26905                 }
26906                 oldParent.removeChild(node);
26907             }
26908             
26909             index = this.childNodes.length;
26910             if(index == 0){
26911                 this.setFirstChild(node);
26912             }
26913             this.childNodes.push(node);
26914             node.parentNode = this;
26915             var ps = this.childNodes[index-1];
26916             if(ps){
26917                 node.previousSibling = ps;
26918                 ps.nextSibling = node;
26919             }else{
26920                 node.previousSibling = null;
26921             }
26922             node.nextSibling = null;
26923             this.setLastChild(node);
26924             node.setOwnerTree(this.getOwnerTree());
26925             this.fireEvent("append", this.ownerTree, this, node, index);
26926             if(this.ownerTree) {
26927                 this.ownerTree.fireEvent("appendnode", this, node, index);
26928             }
26929             if(oldParent){
26930                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26931             }
26932             return node;
26933         }
26934     },
26935
26936     /**
26937      * Removes a child node from this node.
26938      * @param {Node} node The node to remove
26939      * @return {Node} The removed node
26940      */
26941     removeChild : function(node){
26942         var index = this.childNodes.indexOf(node);
26943         if(index == -1){
26944             return false;
26945         }
26946         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26947             return false;
26948         }
26949
26950         // remove it from childNodes collection
26951         this.childNodes.splice(index, 1);
26952
26953         // update siblings
26954         if(node.previousSibling){
26955             node.previousSibling.nextSibling = node.nextSibling;
26956         }
26957         if(node.nextSibling){
26958             node.nextSibling.previousSibling = node.previousSibling;
26959         }
26960
26961         // update child refs
26962         if(this.firstChild == node){
26963             this.setFirstChild(node.nextSibling);
26964         }
26965         if(this.lastChild == node){
26966             this.setLastChild(node.previousSibling);
26967         }
26968
26969         node.setOwnerTree(null);
26970         // clear any references from the node
26971         node.parentNode = null;
26972         node.previousSibling = null;
26973         node.nextSibling = null;
26974         this.fireEvent("remove", this.ownerTree, this, node);
26975         return node;
26976     },
26977
26978     /**
26979      * Inserts the first node before the second node in this nodes childNodes collection.
26980      * @param {Node} node The node to insert
26981      * @param {Node} refNode The node to insert before (if null the node is appended)
26982      * @return {Node} The inserted node
26983      */
26984     insertBefore : function(node, refNode){
26985         if(!refNode){ // like standard Dom, refNode can be null for append
26986             return this.appendChild(node);
26987         }
26988         // nothing to do
26989         if(node == refNode){
26990             return false;
26991         }
26992
26993         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26994             return false;
26995         }
26996         var index = this.childNodes.indexOf(refNode);
26997         var oldParent = node.parentNode;
26998         var refIndex = index;
26999
27000         // when moving internally, indexes will change after remove
27001         if(oldParent == this && this.childNodes.indexOf(node) < index){
27002             refIndex--;
27003         }
27004
27005         // it's a move, make sure we move it cleanly
27006         if(oldParent){
27007             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27008                 return false;
27009             }
27010             oldParent.removeChild(node);
27011         }
27012         if(refIndex == 0){
27013             this.setFirstChild(node);
27014         }
27015         this.childNodes.splice(refIndex, 0, node);
27016         node.parentNode = this;
27017         var ps = this.childNodes[refIndex-1];
27018         if(ps){
27019             node.previousSibling = ps;
27020             ps.nextSibling = node;
27021         }else{
27022             node.previousSibling = null;
27023         }
27024         node.nextSibling = refNode;
27025         refNode.previousSibling = node;
27026         node.setOwnerTree(this.getOwnerTree());
27027         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27028         if(oldParent){
27029             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27030         }
27031         return node;
27032     },
27033
27034     /**
27035      * Returns the child node at the specified index.
27036      * @param {Number} index
27037      * @return {Node}
27038      */
27039     item : function(index){
27040         return this.childNodes[index];
27041     },
27042
27043     /**
27044      * Replaces one child node in this node with another.
27045      * @param {Node} newChild The replacement node
27046      * @param {Node} oldChild The node to replace
27047      * @return {Node} The replaced node
27048      */
27049     replaceChild : function(newChild, oldChild){
27050         this.insertBefore(newChild, oldChild);
27051         this.removeChild(oldChild);
27052         return oldChild;
27053     },
27054
27055     /**
27056      * Returns the index of a child node
27057      * @param {Node} node
27058      * @return {Number} The index of the node or -1 if it was not found
27059      */
27060     indexOf : function(child){
27061         return this.childNodes.indexOf(child);
27062     },
27063
27064     /**
27065      * Returns the tree this node is in.
27066      * @return {Tree}
27067      */
27068     getOwnerTree : function(){
27069         // if it doesn't have one, look for one
27070         if(!this.ownerTree){
27071             var p = this;
27072             while(p){
27073                 if(p.ownerTree){
27074                     this.ownerTree = p.ownerTree;
27075                     break;
27076                 }
27077                 p = p.parentNode;
27078             }
27079         }
27080         return this.ownerTree;
27081     },
27082
27083     /**
27084      * Returns depth of this node (the root node has a depth of 0)
27085      * @return {Number}
27086      */
27087     getDepth : function(){
27088         var depth = 0;
27089         var p = this;
27090         while(p.parentNode){
27091             ++depth;
27092             p = p.parentNode;
27093         }
27094         return depth;
27095     },
27096
27097     // private
27098     setOwnerTree : function(tree){
27099         // if it's move, we need to update everyone
27100         if(tree != this.ownerTree){
27101             if(this.ownerTree){
27102                 this.ownerTree.unregisterNode(this);
27103             }
27104             this.ownerTree = tree;
27105             var cs = this.childNodes;
27106             for(var i = 0, len = cs.length; i < len; i++) {
27107                 cs[i].setOwnerTree(tree);
27108             }
27109             if(tree){
27110                 tree.registerNode(this);
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27117      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27118      * @return {String} The path
27119      */
27120     getPath : function(attr){
27121         attr = attr || "id";
27122         var p = this.parentNode;
27123         var b = [this.attributes[attr]];
27124         while(p){
27125             b.unshift(p.attributes[attr]);
27126             p = p.parentNode;
27127         }
27128         var sep = this.getOwnerTree().pathSeparator;
27129         return sep + b.join(sep);
27130     },
27131
27132     /**
27133      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27134      * function call will be the scope provided or the current node. The arguments to the function
27135      * will be the args provided or the current node. If the function returns false at any point,
27136      * the bubble is stopped.
27137      * @param {Function} fn The function to call
27138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27140      */
27141     bubble : function(fn, scope, args){
27142         var p = this;
27143         while(p){
27144             if(fn.call(scope || p, args || p) === false){
27145                 break;
27146             }
27147             p = p.parentNode;
27148         }
27149     },
27150
27151     /**
27152      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27153      * function call will be the scope provided or the current node. The arguments to the function
27154      * will be the args provided or the current node. If the function returns false at any point,
27155      * the cascade is stopped on that branch.
27156      * @param {Function} fn The function to call
27157      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27158      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27159      */
27160     cascade : function(fn, scope, args){
27161         if(fn.call(scope || this, args || this) !== false){
27162             var cs = this.childNodes;
27163             for(var i = 0, len = cs.length; i < len; i++) {
27164                 cs[i].cascade(fn, scope, args);
27165             }
27166         }
27167     },
27168
27169     /**
27170      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27171      * function call will be the scope provided or the current node. The arguments to the function
27172      * will be the args provided or the current node. If the function returns false at any point,
27173      * the iteration stops.
27174      * @param {Function} fn The function to call
27175      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27176      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27177      */
27178     eachChild : function(fn, scope, args){
27179         var cs = this.childNodes;
27180         for(var i = 0, len = cs.length; i < len; i++) {
27181                 if(fn.call(scope || this, args || cs[i]) === false){
27182                     break;
27183                 }
27184         }
27185     },
27186
27187     /**
27188      * Finds the first child that has the attribute with the specified value.
27189      * @param {String} attribute The attribute name
27190      * @param {Mixed} value The value to search for
27191      * @return {Node} The found child or null if none was found
27192      */
27193     findChild : function(attribute, value){
27194         var cs = this.childNodes;
27195         for(var i = 0, len = cs.length; i < len; i++) {
27196                 if(cs[i].attributes[attribute] == value){
27197                     return cs[i];
27198                 }
27199         }
27200         return null;
27201     },
27202
27203     /**
27204      * Finds the first child by a custom function. The child matches if the function passed
27205      * returns true.
27206      * @param {Function} fn
27207      * @param {Object} scope (optional)
27208      * @return {Node} The found child or null if none was found
27209      */
27210     findChildBy : function(fn, scope){
27211         var cs = this.childNodes;
27212         for(var i = 0, len = cs.length; i < len; i++) {
27213                 if(fn.call(scope||cs[i], cs[i]) === true){
27214                     return cs[i];
27215                 }
27216         }
27217         return null;
27218     },
27219
27220     /**
27221      * Sorts this nodes children using the supplied sort function
27222      * @param {Function} fn
27223      * @param {Object} scope (optional)
27224      */
27225     sort : function(fn, scope){
27226         var cs = this.childNodes;
27227         var len = cs.length;
27228         if(len > 0){
27229             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27230             cs.sort(sortFn);
27231             for(var i = 0; i < len; i++){
27232                 var n = cs[i];
27233                 n.previousSibling = cs[i-1];
27234                 n.nextSibling = cs[i+1];
27235                 if(i == 0){
27236                     this.setFirstChild(n);
27237                 }
27238                 if(i == len-1){
27239                     this.setLastChild(n);
27240                 }
27241             }
27242         }
27243     },
27244
27245     /**
27246      * Returns true if this node is an ancestor (at any point) of the passed node.
27247      * @param {Node} node
27248      * @return {Boolean}
27249      */
27250     contains : function(node){
27251         return node.isAncestor(this);
27252     },
27253
27254     /**
27255      * Returns true if the passed node is an ancestor (at any point) of this node.
27256      * @param {Node} node
27257      * @return {Boolean}
27258      */
27259     isAncestor : function(node){
27260         var p = this.parentNode;
27261         while(p){
27262             if(p == node){
27263                 return true;
27264             }
27265             p = p.parentNode;
27266         }
27267         return false;
27268     },
27269
27270     toString : function(){
27271         return "[Node"+(this.id?" "+this.id:"")+"]";
27272     }
27273 });/*
27274  * Based on:
27275  * Ext JS Library 1.1.1
27276  * Copyright(c) 2006-2007, Ext JS, LLC.
27277  *
27278  * Originally Released Under LGPL - original licence link has changed is not relivant.
27279  *
27280  * Fork - LGPL
27281  * <script type="text/javascript">
27282  */
27283
27284
27285 /**
27286  * @class Roo.Shadow
27287  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27288  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27289  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27290  * @constructor
27291  * Create a new Shadow
27292  * @param {Object} config The config object
27293  */
27294 Roo.Shadow = function(config){
27295     Roo.apply(this, config);
27296     if(typeof this.mode != "string"){
27297         this.mode = this.defaultMode;
27298     }
27299     var o = this.offset, a = {h: 0};
27300     var rad = Math.floor(this.offset/2);
27301     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27302         case "drop":
27303             a.w = 0;
27304             a.l = a.t = o;
27305             a.t -= 1;
27306             if(Roo.isIE){
27307                 a.l -= this.offset + rad;
27308                 a.t -= this.offset + rad;
27309                 a.w -= rad;
27310                 a.h -= rad;
27311                 a.t += 1;
27312             }
27313         break;
27314         case "sides":
27315             a.w = (o*2);
27316             a.l = -o;
27317             a.t = o-1;
27318             if(Roo.isIE){
27319                 a.l -= (this.offset - rad);
27320                 a.t -= this.offset + rad;
27321                 a.l += 1;
27322                 a.w -= (this.offset - rad)*2;
27323                 a.w -= rad + 1;
27324                 a.h -= 1;
27325             }
27326         break;
27327         case "frame":
27328             a.w = a.h = (o*2);
27329             a.l = a.t = -o;
27330             a.t += 1;
27331             a.h -= 2;
27332             if(Roo.isIE){
27333                 a.l -= (this.offset - rad);
27334                 a.t -= (this.offset - rad);
27335                 a.l += 1;
27336                 a.w -= (this.offset + rad + 1);
27337                 a.h -= (this.offset + rad);
27338                 a.h += 1;
27339             }
27340         break;
27341     };
27342
27343     this.adjusts = a;
27344 };
27345
27346 Roo.Shadow.prototype = {
27347     /**
27348      * @cfg {String} mode
27349      * The shadow display mode.  Supports the following options:<br />
27350      * sides: Shadow displays on both sides and bottom only<br />
27351      * frame: Shadow displays equally on all four sides<br />
27352      * drop: Traditional bottom-right drop shadow (default)
27353      */
27354     mode: false,
27355     /**
27356      * @cfg {String} offset
27357      * The number of pixels to offset the shadow from the element (defaults to 4)
27358      */
27359     offset: 4,
27360
27361     // private
27362     defaultMode: "drop",
27363
27364     /**
27365      * Displays the shadow under the target element
27366      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27367      */
27368     show : function(target){
27369         target = Roo.get(target);
27370         if(!this.el){
27371             this.el = Roo.Shadow.Pool.pull();
27372             if(this.el.dom.nextSibling != target.dom){
27373                 this.el.insertBefore(target);
27374             }
27375         }
27376         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27377         if(Roo.isIE){
27378             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27379         }
27380         this.realign(
27381             target.getLeft(true),
27382             target.getTop(true),
27383             target.getWidth(),
27384             target.getHeight()
27385         );
27386         this.el.dom.style.display = "block";
27387     },
27388
27389     /**
27390      * Returns true if the shadow is visible, else false
27391      */
27392     isVisible : function(){
27393         return this.el ? true : false;  
27394     },
27395
27396     /**
27397      * Direct alignment when values are already available. Show must be called at least once before
27398      * calling this method to ensure it is initialized.
27399      * @param {Number} left The target element left position
27400      * @param {Number} top The target element top position
27401      * @param {Number} width The target element width
27402      * @param {Number} height The target element height
27403      */
27404     realign : function(l, t, w, h){
27405         if(!this.el){
27406             return;
27407         }
27408         var a = this.adjusts, d = this.el.dom, s = d.style;
27409         var iea = 0;
27410         s.left = (l+a.l)+"px";
27411         s.top = (t+a.t)+"px";
27412         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27413  
27414         if(s.width != sws || s.height != shs){
27415             s.width = sws;
27416             s.height = shs;
27417             if(!Roo.isIE){
27418                 var cn = d.childNodes;
27419                 var sww = Math.max(0, (sw-12))+"px";
27420                 cn[0].childNodes[1].style.width = sww;
27421                 cn[1].childNodes[1].style.width = sww;
27422                 cn[2].childNodes[1].style.width = sww;
27423                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27424             }
27425         }
27426     },
27427
27428     /**
27429      * Hides this shadow
27430      */
27431     hide : function(){
27432         if(this.el){
27433             this.el.dom.style.display = "none";
27434             Roo.Shadow.Pool.push(this.el);
27435             delete this.el;
27436         }
27437     },
27438
27439     /**
27440      * Adjust the z-index of this shadow
27441      * @param {Number} zindex The new z-index
27442      */
27443     setZIndex : function(z){
27444         this.zIndex = z;
27445         if(this.el){
27446             this.el.setStyle("z-index", z);
27447         }
27448     }
27449 };
27450
27451 // Private utility class that manages the internal Shadow cache
27452 Roo.Shadow.Pool = function(){
27453     var p = [];
27454     var markup = Roo.isIE ?
27455                  '<div class="x-ie-shadow"></div>' :
27456                  '<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>';
27457     return {
27458         pull : function(){
27459             var sh = p.shift();
27460             if(!sh){
27461                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27462                 sh.autoBoxAdjust = false;
27463             }
27464             return sh;
27465         },
27466
27467         push : function(sh){
27468             p.push(sh);
27469         }
27470     };
27471 }();/*
27472  * Based on:
27473  * Ext JS Library 1.1.1
27474  * Copyright(c) 2006-2007, Ext JS, LLC.
27475  *
27476  * Originally Released Under LGPL - original licence link has changed is not relivant.
27477  *
27478  * Fork - LGPL
27479  * <script type="text/javascript">
27480  */
27481
27482
27483 /**
27484  * @class Roo.SplitBar
27485  * @extends Roo.util.Observable
27486  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27487  * <br><br>
27488  * Usage:
27489  * <pre><code>
27490 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27491                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27492 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27493 split.minSize = 100;
27494 split.maxSize = 600;
27495 split.animate = true;
27496 split.on('moved', splitterMoved);
27497 </code></pre>
27498  * @constructor
27499  * Create a new SplitBar
27500  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27501  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27502  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27503  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27504                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27505                         position of the SplitBar).
27506  */
27507 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27508     
27509     /** @private */
27510     this.el = Roo.get(dragElement, true);
27511     this.el.dom.unselectable = "on";
27512     /** @private */
27513     this.resizingEl = Roo.get(resizingElement, true);
27514
27515     /**
27516      * @private
27517      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27518      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27519      * @type Number
27520      */
27521     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27522     
27523     /**
27524      * The minimum size of the resizing element. (Defaults to 0)
27525      * @type Number
27526      */
27527     this.minSize = 0;
27528     
27529     /**
27530      * The maximum size of the resizing element. (Defaults to 2000)
27531      * @type Number
27532      */
27533     this.maxSize = 2000;
27534     
27535     /**
27536      * Whether to animate the transition to the new size
27537      * @type Boolean
27538      */
27539     this.animate = false;
27540     
27541     /**
27542      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27543      * @type Boolean
27544      */
27545     this.useShim = false;
27546     
27547     /** @private */
27548     this.shim = null;
27549     
27550     if(!existingProxy){
27551         /** @private */
27552         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27553     }else{
27554         this.proxy = Roo.get(existingProxy).dom;
27555     }
27556     /** @private */
27557     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27558     
27559     /** @private */
27560     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27561     
27562     /** @private */
27563     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27564     
27565     /** @private */
27566     this.dragSpecs = {};
27567     
27568     /**
27569      * @private The adapter to use to positon and resize elements
27570      */
27571     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27572     this.adapter.init(this);
27573     
27574     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27575         /** @private */
27576         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27577         this.el.addClass("x-splitbar-h");
27578     }else{
27579         /** @private */
27580         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27581         this.el.addClass("x-splitbar-v");
27582     }
27583     
27584     this.addEvents({
27585         /**
27586          * @event resize
27587          * Fires when the splitter is moved (alias for {@link #event-moved})
27588          * @param {Roo.SplitBar} this
27589          * @param {Number} newSize the new width or height
27590          */
27591         "resize" : true,
27592         /**
27593          * @event moved
27594          * Fires when the splitter is moved
27595          * @param {Roo.SplitBar} this
27596          * @param {Number} newSize the new width or height
27597          */
27598         "moved" : true,
27599         /**
27600          * @event beforeresize
27601          * Fires before the splitter is dragged
27602          * @param {Roo.SplitBar} this
27603          */
27604         "beforeresize" : true,
27605
27606         "beforeapply" : true
27607     });
27608
27609     Roo.util.Observable.call(this);
27610 };
27611
27612 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27613     onStartProxyDrag : function(x, y){
27614         this.fireEvent("beforeresize", this);
27615         if(!this.overlay){
27616             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27617             o.unselectable();
27618             o.enableDisplayMode("block");
27619             // all splitbars share the same overlay
27620             Roo.SplitBar.prototype.overlay = o;
27621         }
27622         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27623         this.overlay.show();
27624         Roo.get(this.proxy).setDisplayed("block");
27625         var size = this.adapter.getElementSize(this);
27626         this.activeMinSize = this.getMinimumSize();;
27627         this.activeMaxSize = this.getMaximumSize();;
27628         var c1 = size - this.activeMinSize;
27629         var c2 = Math.max(this.activeMaxSize - size, 0);
27630         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27631             this.dd.resetConstraints();
27632             this.dd.setXConstraint(
27633                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27634                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27635             );
27636             this.dd.setYConstraint(0, 0);
27637         }else{
27638             this.dd.resetConstraints();
27639             this.dd.setXConstraint(0, 0);
27640             this.dd.setYConstraint(
27641                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27642                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27643             );
27644          }
27645         this.dragSpecs.startSize = size;
27646         this.dragSpecs.startPoint = [x, y];
27647         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27648     },
27649     
27650     /** 
27651      * @private Called after the drag operation by the DDProxy
27652      */
27653     onEndProxyDrag : function(e){
27654         Roo.get(this.proxy).setDisplayed(false);
27655         var endPoint = Roo.lib.Event.getXY(e);
27656         if(this.overlay){
27657             this.overlay.hide();
27658         }
27659         var newSize;
27660         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27661             newSize = this.dragSpecs.startSize + 
27662                 (this.placement == Roo.SplitBar.LEFT ?
27663                     endPoint[0] - this.dragSpecs.startPoint[0] :
27664                     this.dragSpecs.startPoint[0] - endPoint[0]
27665                 );
27666         }else{
27667             newSize = this.dragSpecs.startSize + 
27668                 (this.placement == Roo.SplitBar.TOP ?
27669                     endPoint[1] - this.dragSpecs.startPoint[1] :
27670                     this.dragSpecs.startPoint[1] - endPoint[1]
27671                 );
27672         }
27673         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27674         if(newSize != this.dragSpecs.startSize){
27675             if(this.fireEvent('beforeapply', this, newSize) !== false){
27676                 this.adapter.setElementSize(this, newSize);
27677                 this.fireEvent("moved", this, newSize);
27678                 this.fireEvent("resize", this, newSize);
27679             }
27680         }
27681     },
27682     
27683     /**
27684      * Get the adapter this SplitBar uses
27685      * @return The adapter object
27686      */
27687     getAdapter : function(){
27688         return this.adapter;
27689     },
27690     
27691     /**
27692      * Set the adapter this SplitBar uses
27693      * @param {Object} adapter A SplitBar adapter object
27694      */
27695     setAdapter : function(adapter){
27696         this.adapter = adapter;
27697         this.adapter.init(this);
27698     },
27699     
27700     /**
27701      * Gets the minimum size for the resizing element
27702      * @return {Number} The minimum size
27703      */
27704     getMinimumSize : function(){
27705         return this.minSize;
27706     },
27707     
27708     /**
27709      * Sets the minimum size for the resizing element
27710      * @param {Number} minSize The minimum size
27711      */
27712     setMinimumSize : function(minSize){
27713         this.minSize = minSize;
27714     },
27715     
27716     /**
27717      * Gets the maximum size for the resizing element
27718      * @return {Number} The maximum size
27719      */
27720     getMaximumSize : function(){
27721         return this.maxSize;
27722     },
27723     
27724     /**
27725      * Sets the maximum size for the resizing element
27726      * @param {Number} maxSize The maximum size
27727      */
27728     setMaximumSize : function(maxSize){
27729         this.maxSize = maxSize;
27730     },
27731     
27732     /**
27733      * Sets the initialize size for the resizing element
27734      * @param {Number} size The initial size
27735      */
27736     setCurrentSize : function(size){
27737         var oldAnimate = this.animate;
27738         this.animate = false;
27739         this.adapter.setElementSize(this, size);
27740         this.animate = oldAnimate;
27741     },
27742     
27743     /**
27744      * Destroy this splitbar. 
27745      * @param {Boolean} removeEl True to remove the element
27746      */
27747     destroy : function(removeEl){
27748         if(this.shim){
27749             this.shim.remove();
27750         }
27751         this.dd.unreg();
27752         this.proxy.parentNode.removeChild(this.proxy);
27753         if(removeEl){
27754             this.el.remove();
27755         }
27756     }
27757 });
27758
27759 /**
27760  * @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.
27761  */
27762 Roo.SplitBar.createProxy = function(dir){
27763     var proxy = new Roo.Element(document.createElement("div"));
27764     proxy.unselectable();
27765     var cls = 'x-splitbar-proxy';
27766     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27767     document.body.appendChild(proxy.dom);
27768     return proxy.dom;
27769 };
27770
27771 /** 
27772  * @class Roo.SplitBar.BasicLayoutAdapter
27773  * Default Adapter. It assumes the splitter and resizing element are not positioned
27774  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27775  */
27776 Roo.SplitBar.BasicLayoutAdapter = function(){
27777 };
27778
27779 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27780     // do nothing for now
27781     init : function(s){
27782     
27783     },
27784     /**
27785      * Called before drag operations to get the current size of the resizing element. 
27786      * @param {Roo.SplitBar} s The SplitBar using this adapter
27787      */
27788      getElementSize : function(s){
27789         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27790             return s.resizingEl.getWidth();
27791         }else{
27792             return s.resizingEl.getHeight();
27793         }
27794     },
27795     
27796     /**
27797      * Called after drag operations to set the size of the resizing element.
27798      * @param {Roo.SplitBar} s The SplitBar using this adapter
27799      * @param {Number} newSize The new size to set
27800      * @param {Function} onComplete A function to be invoked when resizing is complete
27801      */
27802     setElementSize : function(s, newSize, onComplete){
27803         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27804             if(!s.animate){
27805                 s.resizingEl.setWidth(newSize);
27806                 if(onComplete){
27807                     onComplete(s, newSize);
27808                 }
27809             }else{
27810                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27811             }
27812         }else{
27813             
27814             if(!s.animate){
27815                 s.resizingEl.setHeight(newSize);
27816                 if(onComplete){
27817                     onComplete(s, newSize);
27818                 }
27819             }else{
27820                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27821             }
27822         }
27823     }
27824 };
27825
27826 /** 
27827  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27828  * @extends Roo.SplitBar.BasicLayoutAdapter
27829  * Adapter that  moves the splitter element to align with the resized sizing element. 
27830  * Used with an absolute positioned SplitBar.
27831  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27832  * document.body, make sure you assign an id to the body element.
27833  */
27834 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27835     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27836     this.container = Roo.get(container);
27837 };
27838
27839 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27840     init : function(s){
27841         this.basic.init(s);
27842     },
27843     
27844     getElementSize : function(s){
27845         return this.basic.getElementSize(s);
27846     },
27847     
27848     setElementSize : function(s, newSize, onComplete){
27849         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27850     },
27851     
27852     moveSplitter : function(s){
27853         var yes = Roo.SplitBar;
27854         switch(s.placement){
27855             case yes.LEFT:
27856                 s.el.setX(s.resizingEl.getRight());
27857                 break;
27858             case yes.RIGHT:
27859                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27860                 break;
27861             case yes.TOP:
27862                 s.el.setY(s.resizingEl.getBottom());
27863                 break;
27864             case yes.BOTTOM:
27865                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27866                 break;
27867         }
27868     }
27869 };
27870
27871 /**
27872  * Orientation constant - Create a vertical SplitBar
27873  * @static
27874  * @type Number
27875  */
27876 Roo.SplitBar.VERTICAL = 1;
27877
27878 /**
27879  * Orientation constant - Create a horizontal SplitBar
27880  * @static
27881  * @type Number
27882  */
27883 Roo.SplitBar.HORIZONTAL = 2;
27884
27885 /**
27886  * Placement constant - The resizing element is to the left of the splitter element
27887  * @static
27888  * @type Number
27889  */
27890 Roo.SplitBar.LEFT = 1;
27891
27892 /**
27893  * Placement constant - The resizing element is to the right of the splitter element
27894  * @static
27895  * @type Number
27896  */
27897 Roo.SplitBar.RIGHT = 2;
27898
27899 /**
27900  * Placement constant - The resizing element is positioned above the splitter element
27901  * @static
27902  * @type Number
27903  */
27904 Roo.SplitBar.TOP = 3;
27905
27906 /**
27907  * Placement constant - The resizing element is positioned under splitter element
27908  * @static
27909  * @type Number
27910  */
27911 Roo.SplitBar.BOTTOM = 4;
27912 /*
27913  * Based on:
27914  * Ext JS Library 1.1.1
27915  * Copyright(c) 2006-2007, Ext JS, LLC.
27916  *
27917  * Originally Released Under LGPL - original licence link has changed is not relivant.
27918  *
27919  * Fork - LGPL
27920  * <script type="text/javascript">
27921  */
27922
27923 /**
27924  * @class Roo.View
27925  * @extends Roo.util.Observable
27926  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27927  * This class also supports single and multi selection modes. <br>
27928  * Create a data model bound view:
27929  <pre><code>
27930  var store = new Roo.data.Store(...);
27931
27932  var view = new Roo.View({
27933     el : "my-element",
27934     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27935  
27936     singleSelect: true,
27937     selectedClass: "ydataview-selected",
27938     store: store
27939  });
27940
27941  // listen for node click?
27942  view.on("click", function(vw, index, node, e){
27943  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27944  });
27945
27946  // load XML data
27947  dataModel.load("foobar.xml");
27948  </code></pre>
27949  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27950  * <br><br>
27951  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27952  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27953  * 
27954  * Note: old style constructor is still suported (container, template, config)
27955  * 
27956  * @constructor
27957  * Create a new View
27958  * @param {Object} config The config object
27959  * 
27960  */
27961 Roo.View = function(config, depreciated_tpl, depreciated_config){
27962     
27963     this.parent = false;
27964     
27965     if (typeof(depreciated_tpl) == 'undefined') {
27966         // new way.. - universal constructor.
27967         Roo.apply(this, config);
27968         this.el  = Roo.get(this.el);
27969     } else {
27970         // old format..
27971         this.el  = Roo.get(config);
27972         this.tpl = depreciated_tpl;
27973         Roo.apply(this, depreciated_config);
27974     }
27975     this.wrapEl  = this.el.wrap().wrap();
27976     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27977     
27978     
27979     if(typeof(this.tpl) == "string"){
27980         this.tpl = new Roo.Template(this.tpl);
27981     } else {
27982         // support xtype ctors..
27983         this.tpl = new Roo.factory(this.tpl, Roo);
27984     }
27985     
27986     
27987     this.tpl.compile();
27988     
27989     /** @private */
27990     this.addEvents({
27991         /**
27992          * @event beforeclick
27993          * Fires before a click is processed. Returns false to cancel the default action.
27994          * @param {Roo.View} this
27995          * @param {Number} index The index of the target node
27996          * @param {HTMLElement} node The target node
27997          * @param {Roo.EventObject} e The raw event object
27998          */
27999             "beforeclick" : true,
28000         /**
28001          * @event click
28002          * Fires when a template node is clicked.
28003          * @param {Roo.View} this
28004          * @param {Number} index The index of the target node
28005          * @param {HTMLElement} node The target node
28006          * @param {Roo.EventObject} e The raw event object
28007          */
28008             "click" : true,
28009         /**
28010          * @event dblclick
28011          * Fires when a template node is double clicked.
28012          * @param {Roo.View} this
28013          * @param {Number} index The index of the target node
28014          * @param {HTMLElement} node The target node
28015          * @param {Roo.EventObject} e The raw event object
28016          */
28017             "dblclick" : true,
28018         /**
28019          * @event contextmenu
28020          * Fires when a template node is right clicked.
28021          * @param {Roo.View} this
28022          * @param {Number} index The index of the target node
28023          * @param {HTMLElement} node The target node
28024          * @param {Roo.EventObject} e The raw event object
28025          */
28026             "contextmenu" : true,
28027         /**
28028          * @event selectionchange
28029          * Fires when the selected nodes change.
28030          * @param {Roo.View} this
28031          * @param {Array} selections Array of the selected nodes
28032          */
28033             "selectionchange" : true,
28034     
28035         /**
28036          * @event beforeselect
28037          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28038          * @param {Roo.View} this
28039          * @param {HTMLElement} node The node to be selected
28040          * @param {Array} selections Array of currently selected nodes
28041          */
28042             "beforeselect" : true,
28043         /**
28044          * @event preparedata
28045          * Fires on every row to render, to allow you to change the data.
28046          * @param {Roo.View} this
28047          * @param {Object} data to be rendered (change this)
28048          */
28049           "preparedata" : true
28050           
28051           
28052         });
28053
28054
28055
28056     this.el.on({
28057         "click": this.onClick,
28058         "dblclick": this.onDblClick,
28059         "contextmenu": this.onContextMenu,
28060         scope:this
28061     });
28062
28063     this.selections = [];
28064     this.nodes = [];
28065     this.cmp = new Roo.CompositeElementLite([]);
28066     if(this.store){
28067         this.store = Roo.factory(this.store, Roo.data);
28068         this.setStore(this.store, true);
28069     }
28070     
28071     if ( this.footer && this.footer.xtype) {
28072            
28073          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28074         
28075         this.footer.dataSource = this.store;
28076         this.footer.container = fctr;
28077         this.footer = Roo.factory(this.footer, Roo);
28078         fctr.insertFirst(this.el);
28079         
28080         // this is a bit insane - as the paging toolbar seems to detach the el..
28081 //        dom.parentNode.parentNode.parentNode
28082          // they get detached?
28083     }
28084     
28085     
28086     Roo.View.superclass.constructor.call(this);
28087     
28088     
28089 };
28090
28091 Roo.extend(Roo.View, Roo.util.Observable, {
28092     
28093      /**
28094      * @cfg {Roo.data.Store} store Data store to load data from.
28095      */
28096     store : false,
28097     
28098     /**
28099      * @cfg {String|Roo.Element} el The container element.
28100      */
28101     el : '',
28102     
28103     /**
28104      * @cfg {String|Roo.Template} tpl The template used by this View 
28105      */
28106     tpl : false,
28107     /**
28108      * @cfg {String} dataName the named area of the template to use as the data area
28109      *                          Works with domtemplates roo-name="name"
28110      */
28111     dataName: false,
28112     /**
28113      * @cfg {String} selectedClass The css class to add to selected nodes
28114      */
28115     selectedClass : "x-view-selected",
28116      /**
28117      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28118      */
28119     emptyText : "",
28120     
28121     /**
28122      * @cfg {String} text to display on mask (default Loading)
28123      */
28124     mask : false,
28125     /**
28126      * @cfg {Boolean} multiSelect Allow multiple selection
28127      */
28128     multiSelect : false,
28129     /**
28130      * @cfg {Boolean} singleSelect Allow single selection
28131      */
28132     singleSelect:  false,
28133     
28134     /**
28135      * @cfg {Boolean} toggleSelect - selecting 
28136      */
28137     toggleSelect : false,
28138     
28139     /**
28140      * @cfg {Boolean} tickable - selecting 
28141      */
28142     tickable : false,
28143     
28144     /**
28145      * Returns the element this view is bound to.
28146      * @return {Roo.Element}
28147      */
28148     getEl : function(){
28149         return this.wrapEl;
28150     },
28151     
28152     
28153
28154     /**
28155      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28156      */
28157     refresh : function(){
28158         //Roo.log('refresh');
28159         var t = this.tpl;
28160         
28161         // if we are using something like 'domtemplate', then
28162         // the what gets used is:
28163         // t.applySubtemplate(NAME, data, wrapping data..)
28164         // the outer template then get' applied with
28165         //     the store 'extra data'
28166         // and the body get's added to the
28167         //      roo-name="data" node?
28168         //      <span class='roo-tpl-{name}'></span> ?????
28169         
28170         
28171         
28172         this.clearSelections();
28173         this.el.update("");
28174         var html = [];
28175         var records = this.store.getRange();
28176         if(records.length < 1) {
28177             
28178             // is this valid??  = should it render a template??
28179             
28180             this.el.update(this.emptyText);
28181             return;
28182         }
28183         var el = this.el;
28184         if (this.dataName) {
28185             this.el.update(t.apply(this.store.meta)); //????
28186             el = this.el.child('.roo-tpl-' + this.dataName);
28187         }
28188         
28189         for(var i = 0, len = records.length; i < len; i++){
28190             var data = this.prepareData(records[i].data, i, records[i]);
28191             this.fireEvent("preparedata", this, data, i, records[i]);
28192             
28193             var d = Roo.apply({}, data);
28194             
28195             if(this.tickable){
28196                 Roo.apply(d, {'roo-id' : Roo.id()});
28197                 
28198                 var _this = this;
28199             
28200                 Roo.each(this.parent.item, function(item){
28201                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28202                         return;
28203                     }
28204                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28205                 });
28206             }
28207             
28208             html[html.length] = Roo.util.Format.trim(
28209                 this.dataName ?
28210                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28211                     t.apply(d)
28212             );
28213         }
28214         
28215         
28216         
28217         el.update(html.join(""));
28218         this.nodes = el.dom.childNodes;
28219         this.updateIndexes(0);
28220     },
28221     
28222
28223     /**
28224      * Function to override to reformat the data that is sent to
28225      * the template for each node.
28226      * DEPRICATED - use the preparedata event handler.
28227      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28228      * a JSON object for an UpdateManager bound view).
28229      */
28230     prepareData : function(data, index, record)
28231     {
28232         this.fireEvent("preparedata", this, data, index, record);
28233         return data;
28234     },
28235
28236     onUpdate : function(ds, record){
28237         // Roo.log('on update');   
28238         this.clearSelections();
28239         var index = this.store.indexOf(record);
28240         var n = this.nodes[index];
28241         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28242         n.parentNode.removeChild(n);
28243         this.updateIndexes(index, index);
28244     },
28245
28246     
28247     
28248 // --------- FIXME     
28249     onAdd : function(ds, records, index)
28250     {
28251         //Roo.log(['on Add', ds, records, index] );        
28252         this.clearSelections();
28253         if(this.nodes.length == 0){
28254             this.refresh();
28255             return;
28256         }
28257         var n = this.nodes[index];
28258         for(var i = 0, len = records.length; i < len; i++){
28259             var d = this.prepareData(records[i].data, i, records[i]);
28260             if(n){
28261                 this.tpl.insertBefore(n, d);
28262             }else{
28263                 
28264                 this.tpl.append(this.el, d);
28265             }
28266         }
28267         this.updateIndexes(index);
28268     },
28269
28270     onRemove : function(ds, record, index){
28271        // Roo.log('onRemove');
28272         this.clearSelections();
28273         var el = this.dataName  ?
28274             this.el.child('.roo-tpl-' + this.dataName) :
28275             this.el; 
28276         
28277         el.dom.removeChild(this.nodes[index]);
28278         this.updateIndexes(index);
28279     },
28280
28281     /**
28282      * Refresh an individual node.
28283      * @param {Number} index
28284      */
28285     refreshNode : function(index){
28286         this.onUpdate(this.store, this.store.getAt(index));
28287     },
28288
28289     updateIndexes : function(startIndex, endIndex){
28290         var ns = this.nodes;
28291         startIndex = startIndex || 0;
28292         endIndex = endIndex || ns.length - 1;
28293         for(var i = startIndex; i <= endIndex; i++){
28294             ns[i].nodeIndex = i;
28295         }
28296     },
28297
28298     /**
28299      * Changes the data store this view uses and refresh the view.
28300      * @param {Store} store
28301      */
28302     setStore : function(store, initial){
28303         if(!initial && this.store){
28304             this.store.un("datachanged", this.refresh);
28305             this.store.un("add", this.onAdd);
28306             this.store.un("remove", this.onRemove);
28307             this.store.un("update", this.onUpdate);
28308             this.store.un("clear", this.refresh);
28309             this.store.un("beforeload", this.onBeforeLoad);
28310             this.store.un("load", this.onLoad);
28311             this.store.un("loadexception", this.onLoad);
28312         }
28313         if(store){
28314           
28315             store.on("datachanged", this.refresh, this);
28316             store.on("add", this.onAdd, this);
28317             store.on("remove", this.onRemove, this);
28318             store.on("update", this.onUpdate, this);
28319             store.on("clear", this.refresh, this);
28320             store.on("beforeload", this.onBeforeLoad, this);
28321             store.on("load", this.onLoad, this);
28322             store.on("loadexception", this.onLoad, this);
28323         }
28324         
28325         if(store){
28326             this.refresh();
28327         }
28328     },
28329     /**
28330      * onbeforeLoad - masks the loading area.
28331      *
28332      */
28333     onBeforeLoad : function(store,opts)
28334     {
28335          //Roo.log('onBeforeLoad');   
28336         if (!opts.add) {
28337             this.el.update("");
28338         }
28339         this.el.mask(this.mask ? this.mask : "Loading" ); 
28340     },
28341     onLoad : function ()
28342     {
28343         this.el.unmask();
28344     },
28345     
28346
28347     /**
28348      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28349      * @param {HTMLElement} node
28350      * @return {HTMLElement} The template node
28351      */
28352     findItemFromChild : function(node){
28353         var el = this.dataName  ?
28354             this.el.child('.roo-tpl-' + this.dataName,true) :
28355             this.el.dom; 
28356         
28357         if(!node || node.parentNode == el){
28358                     return node;
28359             }
28360             var p = node.parentNode;
28361             while(p && p != el){
28362             if(p.parentNode == el){
28363                 return p;
28364             }
28365             p = p.parentNode;
28366         }
28367             return null;
28368     },
28369
28370     /** @ignore */
28371     onClick : function(e){
28372         var item = this.findItemFromChild(e.getTarget());
28373         if(item){
28374             var index = this.indexOf(item);
28375             if(this.onItemClick(item, index, e) !== false){
28376                 this.fireEvent("click", this, index, item, e);
28377             }
28378         }else{
28379             this.clearSelections();
28380         }
28381     },
28382
28383     /** @ignore */
28384     onContextMenu : function(e){
28385         var item = this.findItemFromChild(e.getTarget());
28386         if(item){
28387             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28388         }
28389     },
28390
28391     /** @ignore */
28392     onDblClick : function(e){
28393         var item = this.findItemFromChild(e.getTarget());
28394         if(item){
28395             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28396         }
28397     },
28398
28399     onItemClick : function(item, index, e)
28400     {
28401         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28402             return false;
28403         }
28404         if (this.toggleSelect) {
28405             var m = this.isSelected(item) ? 'unselect' : 'select';
28406             //Roo.log(m);
28407             var _t = this;
28408             _t[m](item, true, false);
28409             return true;
28410         }
28411         if(this.multiSelect || this.singleSelect){
28412             if(this.multiSelect && e.shiftKey && this.lastSelection){
28413                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28414             }else{
28415                 this.select(item, this.multiSelect && e.ctrlKey);
28416                 this.lastSelection = item;
28417             }
28418             
28419             if(!this.tickable){
28420                 e.preventDefault();
28421             }
28422             
28423         }
28424         return true;
28425     },
28426
28427     /**
28428      * Get the number of selected nodes.
28429      * @return {Number}
28430      */
28431     getSelectionCount : function(){
28432         return this.selections.length;
28433     },
28434
28435     /**
28436      * Get the currently selected nodes.
28437      * @return {Array} An array of HTMLElements
28438      */
28439     getSelectedNodes : function(){
28440         return this.selections;
28441     },
28442
28443     /**
28444      * Get the indexes of the selected nodes.
28445      * @return {Array}
28446      */
28447     getSelectedIndexes : function(){
28448         var indexes = [], s = this.selections;
28449         for(var i = 0, len = s.length; i < len; i++){
28450             indexes.push(s[i].nodeIndex);
28451         }
28452         return indexes;
28453     },
28454
28455     /**
28456      * Clear all selections
28457      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28458      */
28459     clearSelections : function(suppressEvent){
28460         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28461             this.cmp.elements = this.selections;
28462             this.cmp.removeClass(this.selectedClass);
28463             this.selections = [];
28464             if(!suppressEvent){
28465                 this.fireEvent("selectionchange", this, this.selections);
28466             }
28467         }
28468     },
28469
28470     /**
28471      * Returns true if the passed node is selected
28472      * @param {HTMLElement/Number} node The node or node index
28473      * @return {Boolean}
28474      */
28475     isSelected : function(node){
28476         var s = this.selections;
28477         if(s.length < 1){
28478             return false;
28479         }
28480         node = this.getNode(node);
28481         return s.indexOf(node) !== -1;
28482     },
28483
28484     /**
28485      * Selects nodes.
28486      * @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
28487      * @param {Boolean} keepExisting (optional) true to keep existing selections
28488      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28489      */
28490     select : function(nodeInfo, keepExisting, suppressEvent){
28491         if(nodeInfo instanceof Array){
28492             if(!keepExisting){
28493                 this.clearSelections(true);
28494             }
28495             for(var i = 0, len = nodeInfo.length; i < len; i++){
28496                 this.select(nodeInfo[i], true, true);
28497             }
28498             return;
28499         } 
28500         var node = this.getNode(nodeInfo);
28501         if(!node || this.isSelected(node)){
28502             return; // already selected.
28503         }
28504         if(!keepExisting){
28505             this.clearSelections(true);
28506         }
28507         
28508         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28509             Roo.fly(node).addClass(this.selectedClass);
28510             this.selections.push(node);
28511             if(!suppressEvent){
28512                 this.fireEvent("selectionchange", this, this.selections);
28513             }
28514         }
28515         
28516         
28517     },
28518       /**
28519      * Unselects nodes.
28520      * @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
28521      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28522      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28523      */
28524     unselect : function(nodeInfo, keepExisting, suppressEvent)
28525     {
28526         if(nodeInfo instanceof Array){
28527             Roo.each(this.selections, function(s) {
28528                 this.unselect(s, nodeInfo);
28529             }, this);
28530             return;
28531         }
28532         var node = this.getNode(nodeInfo);
28533         if(!node || !this.isSelected(node)){
28534             //Roo.log("not selected");
28535             return; // not selected.
28536         }
28537         // fireevent???
28538         var ns = [];
28539         Roo.each(this.selections, function(s) {
28540             if (s == node ) {
28541                 Roo.fly(node).removeClass(this.selectedClass);
28542
28543                 return;
28544             }
28545             ns.push(s);
28546         },this);
28547         
28548         this.selections= ns;
28549         this.fireEvent("selectionchange", this, this.selections);
28550     },
28551
28552     /**
28553      * Gets a template node.
28554      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28555      * @return {HTMLElement} The node or null if it wasn't found
28556      */
28557     getNode : function(nodeInfo){
28558         if(typeof nodeInfo == "string"){
28559             return document.getElementById(nodeInfo);
28560         }else if(typeof nodeInfo == "number"){
28561             return this.nodes[nodeInfo];
28562         }
28563         return nodeInfo;
28564     },
28565
28566     /**
28567      * Gets a range template nodes.
28568      * @param {Number} startIndex
28569      * @param {Number} endIndex
28570      * @return {Array} An array of nodes
28571      */
28572     getNodes : function(start, end){
28573         var ns = this.nodes;
28574         start = start || 0;
28575         end = typeof end == "undefined" ? ns.length - 1 : end;
28576         var nodes = [];
28577         if(start <= end){
28578             for(var i = start; i <= end; i++){
28579                 nodes.push(ns[i]);
28580             }
28581         } else{
28582             for(var i = start; i >= end; i--){
28583                 nodes.push(ns[i]);
28584             }
28585         }
28586         return nodes;
28587     },
28588
28589     /**
28590      * Finds the index of the passed node
28591      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28592      * @return {Number} The index of the node or -1
28593      */
28594     indexOf : function(node){
28595         node = this.getNode(node);
28596         if(typeof node.nodeIndex == "number"){
28597             return node.nodeIndex;
28598         }
28599         var ns = this.nodes;
28600         for(var i = 0, len = ns.length; i < len; i++){
28601             if(ns[i] == node){
28602                 return i;
28603             }
28604         }
28605         return -1;
28606     }
28607 });
28608 /*
28609  * Based on:
28610  * Ext JS Library 1.1.1
28611  * Copyright(c) 2006-2007, Ext JS, LLC.
28612  *
28613  * Originally Released Under LGPL - original licence link has changed is not relivant.
28614  *
28615  * Fork - LGPL
28616  * <script type="text/javascript">
28617  */
28618
28619 /**
28620  * @class Roo.JsonView
28621  * @extends Roo.View
28622  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28623 <pre><code>
28624 var view = new Roo.JsonView({
28625     container: "my-element",
28626     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28627     multiSelect: true, 
28628     jsonRoot: "data" 
28629 });
28630
28631 // listen for node click?
28632 view.on("click", function(vw, index, node, e){
28633     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28634 });
28635
28636 // direct load of JSON data
28637 view.load("foobar.php");
28638
28639 // Example from my blog list
28640 var tpl = new Roo.Template(
28641     '&lt;div class="entry"&gt;' +
28642     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28643     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28644     "&lt;/div&gt;&lt;hr /&gt;"
28645 );
28646
28647 var moreView = new Roo.JsonView({
28648     container :  "entry-list", 
28649     template : tpl,
28650     jsonRoot: "posts"
28651 });
28652 moreView.on("beforerender", this.sortEntries, this);
28653 moreView.load({
28654     url: "/blog/get-posts.php",
28655     params: "allposts=true",
28656     text: "Loading Blog Entries..."
28657 });
28658 </code></pre>
28659
28660 * Note: old code is supported with arguments : (container, template, config)
28661
28662
28663  * @constructor
28664  * Create a new JsonView
28665  * 
28666  * @param {Object} config The config object
28667  * 
28668  */
28669 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28670     
28671     
28672     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28673
28674     var um = this.el.getUpdateManager();
28675     um.setRenderer(this);
28676     um.on("update", this.onLoad, this);
28677     um.on("failure", this.onLoadException, this);
28678
28679     /**
28680      * @event beforerender
28681      * Fires before rendering of the downloaded JSON data.
28682      * @param {Roo.JsonView} this
28683      * @param {Object} data The JSON data loaded
28684      */
28685     /**
28686      * @event load
28687      * Fires when data is loaded.
28688      * @param {Roo.JsonView} this
28689      * @param {Object} data The JSON data loaded
28690      * @param {Object} response The raw Connect response object
28691      */
28692     /**
28693      * @event loadexception
28694      * Fires when loading fails.
28695      * @param {Roo.JsonView} this
28696      * @param {Object} response The raw Connect response object
28697      */
28698     this.addEvents({
28699         'beforerender' : true,
28700         'load' : true,
28701         'loadexception' : true
28702     });
28703 };
28704 Roo.extend(Roo.JsonView, Roo.View, {
28705     /**
28706      * @type {String} The root property in the loaded JSON object that contains the data
28707      */
28708     jsonRoot : "",
28709
28710     /**
28711      * Refreshes the view.
28712      */
28713     refresh : function(){
28714         this.clearSelections();
28715         this.el.update("");
28716         var html = [];
28717         var o = this.jsonData;
28718         if(o && o.length > 0){
28719             for(var i = 0, len = o.length; i < len; i++){
28720                 var data = this.prepareData(o[i], i, o);
28721                 html[html.length] = this.tpl.apply(data);
28722             }
28723         }else{
28724             html.push(this.emptyText);
28725         }
28726         this.el.update(html.join(""));
28727         this.nodes = this.el.dom.childNodes;
28728         this.updateIndexes(0);
28729     },
28730
28731     /**
28732      * 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.
28733      * @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:
28734      <pre><code>
28735      view.load({
28736          url: "your-url.php",
28737          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28738          callback: yourFunction,
28739          scope: yourObject, //(optional scope)
28740          discardUrl: false,
28741          nocache: false,
28742          text: "Loading...",
28743          timeout: 30,
28744          scripts: false
28745      });
28746      </code></pre>
28747      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28748      * 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.
28749      * @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}
28750      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28751      * @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.
28752      */
28753     load : function(){
28754         var um = this.el.getUpdateManager();
28755         um.update.apply(um, arguments);
28756     },
28757
28758     // note - render is a standard framework call...
28759     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28760     render : function(el, response){
28761         
28762         this.clearSelections();
28763         this.el.update("");
28764         var o;
28765         try{
28766             if (response != '') {
28767                 o = Roo.util.JSON.decode(response.responseText);
28768                 if(this.jsonRoot){
28769                     
28770                     o = o[this.jsonRoot];
28771                 }
28772             }
28773         } catch(e){
28774         }
28775         /**
28776          * The current JSON data or null
28777          */
28778         this.jsonData = o;
28779         this.beforeRender();
28780         this.refresh();
28781     },
28782
28783 /**
28784  * Get the number of records in the current JSON dataset
28785  * @return {Number}
28786  */
28787     getCount : function(){
28788         return this.jsonData ? this.jsonData.length : 0;
28789     },
28790
28791 /**
28792  * Returns the JSON object for the specified node(s)
28793  * @param {HTMLElement/Array} node The node or an array of nodes
28794  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28795  * you get the JSON object for the node
28796  */
28797     getNodeData : function(node){
28798         if(node instanceof Array){
28799             var data = [];
28800             for(var i = 0, len = node.length; i < len; i++){
28801                 data.push(this.getNodeData(node[i]));
28802             }
28803             return data;
28804         }
28805         return this.jsonData[this.indexOf(node)] || null;
28806     },
28807
28808     beforeRender : function(){
28809         this.snapshot = this.jsonData;
28810         if(this.sortInfo){
28811             this.sort.apply(this, this.sortInfo);
28812         }
28813         this.fireEvent("beforerender", this, this.jsonData);
28814     },
28815
28816     onLoad : function(el, o){
28817         this.fireEvent("load", this, this.jsonData, o);
28818     },
28819
28820     onLoadException : function(el, o){
28821         this.fireEvent("loadexception", this, o);
28822     },
28823
28824 /**
28825  * Filter the data by a specific property.
28826  * @param {String} property A property on your JSON objects
28827  * @param {String/RegExp} value Either string that the property values
28828  * should start with, or a RegExp to test against the property
28829  */
28830     filter : function(property, value){
28831         if(this.jsonData){
28832             var data = [];
28833             var ss = this.snapshot;
28834             if(typeof value == "string"){
28835                 var vlen = value.length;
28836                 if(vlen == 0){
28837                     this.clearFilter();
28838                     return;
28839                 }
28840                 value = value.toLowerCase();
28841                 for(var i = 0, len = ss.length; i < len; i++){
28842                     var o = ss[i];
28843                     if(o[property].substr(0, vlen).toLowerCase() == value){
28844                         data.push(o);
28845                     }
28846                 }
28847             } else if(value.exec){ // regex?
28848                 for(var i = 0, len = ss.length; i < len; i++){
28849                     var o = ss[i];
28850                     if(value.test(o[property])){
28851                         data.push(o);
28852                     }
28853                 }
28854             } else{
28855                 return;
28856             }
28857             this.jsonData = data;
28858             this.refresh();
28859         }
28860     },
28861
28862 /**
28863  * Filter by a function. The passed function will be called with each
28864  * object in the current dataset. If the function returns true the value is kept,
28865  * otherwise it is filtered.
28866  * @param {Function} fn
28867  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28868  */
28869     filterBy : function(fn, scope){
28870         if(this.jsonData){
28871             var data = [];
28872             var ss = this.snapshot;
28873             for(var i = 0, len = ss.length; i < len; i++){
28874                 var o = ss[i];
28875                 if(fn.call(scope || this, o)){
28876                     data.push(o);
28877                 }
28878             }
28879             this.jsonData = data;
28880             this.refresh();
28881         }
28882     },
28883
28884 /**
28885  * Clears the current filter.
28886  */
28887     clearFilter : function(){
28888         if(this.snapshot && this.jsonData != this.snapshot){
28889             this.jsonData = this.snapshot;
28890             this.refresh();
28891         }
28892     },
28893
28894
28895 /**
28896  * Sorts the data for this view and refreshes it.
28897  * @param {String} property A property on your JSON objects to sort on
28898  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28899  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28900  */
28901     sort : function(property, dir, sortType){
28902         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28903         if(this.jsonData){
28904             var p = property;
28905             var dsc = dir && dir.toLowerCase() == "desc";
28906             var f = function(o1, o2){
28907                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28908                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28909                 ;
28910                 if(v1 < v2){
28911                     return dsc ? +1 : -1;
28912                 } else if(v1 > v2){
28913                     return dsc ? -1 : +1;
28914                 } else{
28915                     return 0;
28916                 }
28917             };
28918             this.jsonData.sort(f);
28919             this.refresh();
28920             if(this.jsonData != this.snapshot){
28921                 this.snapshot.sort(f);
28922             }
28923         }
28924     }
28925 });/*
28926  * Based on:
28927  * Ext JS Library 1.1.1
28928  * Copyright(c) 2006-2007, Ext JS, LLC.
28929  *
28930  * Originally Released Under LGPL - original licence link has changed is not relivant.
28931  *
28932  * Fork - LGPL
28933  * <script type="text/javascript">
28934  */
28935  
28936
28937 /**
28938  * @class Roo.ColorPalette
28939  * @extends Roo.Component
28940  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28941  * Here's an example of typical usage:
28942  * <pre><code>
28943 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28944 cp.render('my-div');
28945
28946 cp.on('select', function(palette, selColor){
28947     // do something with selColor
28948 });
28949 </code></pre>
28950  * @constructor
28951  * Create a new ColorPalette
28952  * @param {Object} config The config object
28953  */
28954 Roo.ColorPalette = function(config){
28955     Roo.ColorPalette.superclass.constructor.call(this, config);
28956     this.addEvents({
28957         /**
28958              * @event select
28959              * Fires when a color is selected
28960              * @param {ColorPalette} this
28961              * @param {String} color The 6-digit color hex code (without the # symbol)
28962              */
28963         select: true
28964     });
28965
28966     if(this.handler){
28967         this.on("select", this.handler, this.scope, true);
28968     }
28969 };
28970 Roo.extend(Roo.ColorPalette, Roo.Component, {
28971     /**
28972      * @cfg {String} itemCls
28973      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28974      */
28975     itemCls : "x-color-palette",
28976     /**
28977      * @cfg {String} value
28978      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28979      * the hex codes are case-sensitive.
28980      */
28981     value : null,
28982     clickEvent:'click',
28983     // private
28984     ctype: "Roo.ColorPalette",
28985
28986     /**
28987      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28988      */
28989     allowReselect : false,
28990
28991     /**
28992      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28993      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28994      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28995      * of colors with the width setting until the box is symmetrical.</p>
28996      * <p>You can override individual colors if needed:</p>
28997      * <pre><code>
28998 var cp = new Roo.ColorPalette();
28999 cp.colors[0] = "FF0000";  // change the first box to red
29000 </code></pre>
29001
29002 Or you can provide a custom array of your own for complete control:
29003 <pre><code>
29004 var cp = new Roo.ColorPalette();
29005 cp.colors = ["000000", "993300", "333300"];
29006 </code></pre>
29007      * @type Array
29008      */
29009     colors : [
29010         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29011         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29012         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29013         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29014         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29015     ],
29016
29017     // private
29018     onRender : function(container, position){
29019         var t = new Roo.MasterTemplate(
29020             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29021         );
29022         var c = this.colors;
29023         for(var i = 0, len = c.length; i < len; i++){
29024             t.add([c[i]]);
29025         }
29026         var el = document.createElement("div");
29027         el.className = this.itemCls;
29028         t.overwrite(el);
29029         container.dom.insertBefore(el, position);
29030         this.el = Roo.get(el);
29031         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29032         if(this.clickEvent != 'click'){
29033             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29034         }
29035     },
29036
29037     // private
29038     afterRender : function(){
29039         Roo.ColorPalette.superclass.afterRender.call(this);
29040         if(this.value){
29041             var s = this.value;
29042             this.value = null;
29043             this.select(s);
29044         }
29045     },
29046
29047     // private
29048     handleClick : function(e, t){
29049         e.preventDefault();
29050         if(!this.disabled){
29051             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29052             this.select(c.toUpperCase());
29053         }
29054     },
29055
29056     /**
29057      * Selects the specified color in the palette (fires the select event)
29058      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29059      */
29060     select : function(color){
29061         color = color.replace("#", "");
29062         if(color != this.value || this.allowReselect){
29063             var el = this.el;
29064             if(this.value){
29065                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29066             }
29067             el.child("a.color-"+color).addClass("x-color-palette-sel");
29068             this.value = color;
29069             this.fireEvent("select", this, color);
29070         }
29071     }
29072 });/*
29073  * Based on:
29074  * Ext JS Library 1.1.1
29075  * Copyright(c) 2006-2007, Ext JS, LLC.
29076  *
29077  * Originally Released Under LGPL - original licence link has changed is not relivant.
29078  *
29079  * Fork - LGPL
29080  * <script type="text/javascript">
29081  */
29082  
29083 /**
29084  * @class Roo.DatePicker
29085  * @extends Roo.Component
29086  * Simple date picker class.
29087  * @constructor
29088  * Create a new DatePicker
29089  * @param {Object} config The config object
29090  */
29091 Roo.DatePicker = function(config){
29092     Roo.DatePicker.superclass.constructor.call(this, config);
29093
29094     this.value = config && config.value ?
29095                  config.value.clearTime() : new Date().clearTime();
29096
29097     this.addEvents({
29098         /**
29099              * @event select
29100              * Fires when a date is selected
29101              * @param {DatePicker} this
29102              * @param {Date} date The selected date
29103              */
29104         'select': true,
29105         /**
29106              * @event monthchange
29107              * Fires when the displayed month changes 
29108              * @param {DatePicker} this
29109              * @param {Date} date The selected month
29110              */
29111         'monthchange': true
29112     });
29113
29114     if(this.handler){
29115         this.on("select", this.handler,  this.scope || this);
29116     }
29117     // build the disabledDatesRE
29118     if(!this.disabledDatesRE && this.disabledDates){
29119         var dd = this.disabledDates;
29120         var re = "(?:";
29121         for(var i = 0; i < dd.length; i++){
29122             re += dd[i];
29123             if(i != dd.length-1) {
29124                 re += "|";
29125             }
29126         }
29127         this.disabledDatesRE = new RegExp(re + ")");
29128     }
29129 };
29130
29131 Roo.extend(Roo.DatePicker, Roo.Component, {
29132     /**
29133      * @cfg {String} todayText
29134      * The text to display on the button that selects the current date (defaults to "Today")
29135      */
29136     todayText : "Today",
29137     /**
29138      * @cfg {String} okText
29139      * The text to display on the ok button
29140      */
29141     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29142     /**
29143      * @cfg {String} cancelText
29144      * The text to display on the cancel button
29145      */
29146     cancelText : "Cancel",
29147     /**
29148      * @cfg {String} todayTip
29149      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29150      */
29151     todayTip : "{0} (Spacebar)",
29152     /**
29153      * @cfg {Date} minDate
29154      * Minimum allowable date (JavaScript date object, defaults to null)
29155      */
29156     minDate : null,
29157     /**
29158      * @cfg {Date} maxDate
29159      * Maximum allowable date (JavaScript date object, defaults to null)
29160      */
29161     maxDate : null,
29162     /**
29163      * @cfg {String} minText
29164      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29165      */
29166     minText : "This date is before the minimum date",
29167     /**
29168      * @cfg {String} maxText
29169      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29170      */
29171     maxText : "This date is after the maximum date",
29172     /**
29173      * @cfg {String} format
29174      * The default date format string which can be overriden for localization support.  The format must be
29175      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29176      */
29177     format : "m/d/y",
29178     /**
29179      * @cfg {Array} disabledDays
29180      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29181      */
29182     disabledDays : null,
29183     /**
29184      * @cfg {String} disabledDaysText
29185      * The tooltip to display when the date falls on a disabled day (defaults to "")
29186      */
29187     disabledDaysText : "",
29188     /**
29189      * @cfg {RegExp} disabledDatesRE
29190      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29191      */
29192     disabledDatesRE : null,
29193     /**
29194      * @cfg {String} disabledDatesText
29195      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29196      */
29197     disabledDatesText : "",
29198     /**
29199      * @cfg {Boolean} constrainToViewport
29200      * True to constrain the date picker to the viewport (defaults to true)
29201      */
29202     constrainToViewport : true,
29203     /**
29204      * @cfg {Array} monthNames
29205      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29206      */
29207     monthNames : Date.monthNames,
29208     /**
29209      * @cfg {Array} dayNames
29210      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29211      */
29212     dayNames : Date.dayNames,
29213     /**
29214      * @cfg {String} nextText
29215      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29216      */
29217     nextText: 'Next Month (Control+Right)',
29218     /**
29219      * @cfg {String} prevText
29220      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29221      */
29222     prevText: 'Previous Month (Control+Left)',
29223     /**
29224      * @cfg {String} monthYearText
29225      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29226      */
29227     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29228     /**
29229      * @cfg {Number} startDay
29230      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29231      */
29232     startDay : 0,
29233     /**
29234      * @cfg {Bool} showClear
29235      * Show a clear button (usefull for date form elements that can be blank.)
29236      */
29237     
29238     showClear: false,
29239     
29240     /**
29241      * Sets the value of the date field
29242      * @param {Date} value The date to set
29243      */
29244     setValue : function(value){
29245         var old = this.value;
29246         
29247         if (typeof(value) == 'string') {
29248          
29249             value = Date.parseDate(value, this.format);
29250         }
29251         if (!value) {
29252             value = new Date();
29253         }
29254         
29255         this.value = value.clearTime(true);
29256         if(this.el){
29257             this.update(this.value);
29258         }
29259     },
29260
29261     /**
29262      * Gets the current selected value of the date field
29263      * @return {Date} The selected date
29264      */
29265     getValue : function(){
29266         return this.value;
29267     },
29268
29269     // private
29270     focus : function(){
29271         if(this.el){
29272             this.update(this.activeDate);
29273         }
29274     },
29275
29276     // privateval
29277     onRender : function(container, position){
29278         
29279         var m = [
29280              '<table cellspacing="0">',
29281                 '<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>',
29282                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29283         var dn = this.dayNames;
29284         for(var i = 0; i < 7; i++){
29285             var d = this.startDay+i;
29286             if(d > 6){
29287                 d = d-7;
29288             }
29289             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29290         }
29291         m[m.length] = "</tr></thead><tbody><tr>";
29292         for(var i = 0; i < 42; i++) {
29293             if(i % 7 == 0 && i != 0){
29294                 m[m.length] = "</tr><tr>";
29295             }
29296             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29297         }
29298         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29299             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29300
29301         var el = document.createElement("div");
29302         el.className = "x-date-picker";
29303         el.innerHTML = m.join("");
29304
29305         container.dom.insertBefore(el, position);
29306
29307         this.el = Roo.get(el);
29308         this.eventEl = Roo.get(el.firstChild);
29309
29310         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29311             handler: this.showPrevMonth,
29312             scope: this,
29313             preventDefault:true,
29314             stopDefault:true
29315         });
29316
29317         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29318             handler: this.showNextMonth,
29319             scope: this,
29320             preventDefault:true,
29321             stopDefault:true
29322         });
29323
29324         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29325
29326         this.monthPicker = this.el.down('div.x-date-mp');
29327         this.monthPicker.enableDisplayMode('block');
29328         
29329         var kn = new Roo.KeyNav(this.eventEl, {
29330             "left" : function(e){
29331                 e.ctrlKey ?
29332                     this.showPrevMonth() :
29333                     this.update(this.activeDate.add("d", -1));
29334             },
29335
29336             "right" : function(e){
29337                 e.ctrlKey ?
29338                     this.showNextMonth() :
29339                     this.update(this.activeDate.add("d", 1));
29340             },
29341
29342             "up" : function(e){
29343                 e.ctrlKey ?
29344                     this.showNextYear() :
29345                     this.update(this.activeDate.add("d", -7));
29346             },
29347
29348             "down" : function(e){
29349                 e.ctrlKey ?
29350                     this.showPrevYear() :
29351                     this.update(this.activeDate.add("d", 7));
29352             },
29353
29354             "pageUp" : function(e){
29355                 this.showNextMonth();
29356             },
29357
29358             "pageDown" : function(e){
29359                 this.showPrevMonth();
29360             },
29361
29362             "enter" : function(e){
29363                 e.stopPropagation();
29364                 return true;
29365             },
29366
29367             scope : this
29368         });
29369
29370         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29371
29372         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29373
29374         this.el.unselectable();
29375         
29376         this.cells = this.el.select("table.x-date-inner tbody td");
29377         this.textNodes = this.el.query("table.x-date-inner tbody span");
29378
29379         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29380             text: "&#160;",
29381             tooltip: this.monthYearText
29382         });
29383
29384         this.mbtn.on('click', this.showMonthPicker, this);
29385         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29386
29387
29388         var today = (new Date()).dateFormat(this.format);
29389         
29390         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29391         if (this.showClear) {
29392             baseTb.add( new Roo.Toolbar.Fill());
29393         }
29394         baseTb.add({
29395             text: String.format(this.todayText, today),
29396             tooltip: String.format(this.todayTip, today),
29397             handler: this.selectToday,
29398             scope: this
29399         });
29400         
29401         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29402             
29403         //});
29404         if (this.showClear) {
29405             
29406             baseTb.add( new Roo.Toolbar.Fill());
29407             baseTb.add({
29408                 text: '&#160;',
29409                 cls: 'x-btn-icon x-btn-clear',
29410                 handler: function() {
29411                     //this.value = '';
29412                     this.fireEvent("select", this, '');
29413                 },
29414                 scope: this
29415             });
29416         }
29417         
29418         
29419         if(Roo.isIE){
29420             this.el.repaint();
29421         }
29422         this.update(this.value);
29423     },
29424
29425     createMonthPicker : function(){
29426         if(!this.monthPicker.dom.firstChild){
29427             var buf = ['<table border="0" cellspacing="0">'];
29428             for(var i = 0; i < 6; i++){
29429                 buf.push(
29430                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29431                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29432                     i == 0 ?
29433                     '<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>' :
29434                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29435                 );
29436             }
29437             buf.push(
29438                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29439                     this.okText,
29440                     '</button><button type="button" class="x-date-mp-cancel">',
29441                     this.cancelText,
29442                     '</button></td></tr>',
29443                 '</table>'
29444             );
29445             this.monthPicker.update(buf.join(''));
29446             this.monthPicker.on('click', this.onMonthClick, this);
29447             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29448
29449             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29450             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29451
29452             this.mpMonths.each(function(m, a, i){
29453                 i += 1;
29454                 if((i%2) == 0){
29455                     m.dom.xmonth = 5 + Math.round(i * .5);
29456                 }else{
29457                     m.dom.xmonth = Math.round((i-1) * .5);
29458                 }
29459             });
29460         }
29461     },
29462
29463     showMonthPicker : function(){
29464         this.createMonthPicker();
29465         var size = this.el.getSize();
29466         this.monthPicker.setSize(size);
29467         this.monthPicker.child('table').setSize(size);
29468
29469         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29470         this.updateMPMonth(this.mpSelMonth);
29471         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29472         this.updateMPYear(this.mpSelYear);
29473
29474         this.monthPicker.slideIn('t', {duration:.2});
29475     },
29476
29477     updateMPYear : function(y){
29478         this.mpyear = y;
29479         var ys = this.mpYears.elements;
29480         for(var i = 1; i <= 10; i++){
29481             var td = ys[i-1], y2;
29482             if((i%2) == 0){
29483                 y2 = y + Math.round(i * .5);
29484                 td.firstChild.innerHTML = y2;
29485                 td.xyear = y2;
29486             }else{
29487                 y2 = y - (5-Math.round(i * .5));
29488                 td.firstChild.innerHTML = y2;
29489                 td.xyear = y2;
29490             }
29491             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29492         }
29493     },
29494
29495     updateMPMonth : function(sm){
29496         this.mpMonths.each(function(m, a, i){
29497             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29498         });
29499     },
29500
29501     selectMPMonth: function(m){
29502         
29503     },
29504
29505     onMonthClick : function(e, t){
29506         e.stopEvent();
29507         var el = new Roo.Element(t), pn;
29508         if(el.is('button.x-date-mp-cancel')){
29509             this.hideMonthPicker();
29510         }
29511         else if(el.is('button.x-date-mp-ok')){
29512             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29513             this.hideMonthPicker();
29514         }
29515         else if(pn = el.up('td.x-date-mp-month', 2)){
29516             this.mpMonths.removeClass('x-date-mp-sel');
29517             pn.addClass('x-date-mp-sel');
29518             this.mpSelMonth = pn.dom.xmonth;
29519         }
29520         else if(pn = el.up('td.x-date-mp-year', 2)){
29521             this.mpYears.removeClass('x-date-mp-sel');
29522             pn.addClass('x-date-mp-sel');
29523             this.mpSelYear = pn.dom.xyear;
29524         }
29525         else if(el.is('a.x-date-mp-prev')){
29526             this.updateMPYear(this.mpyear-10);
29527         }
29528         else if(el.is('a.x-date-mp-next')){
29529             this.updateMPYear(this.mpyear+10);
29530         }
29531     },
29532
29533     onMonthDblClick : function(e, t){
29534         e.stopEvent();
29535         var el = new Roo.Element(t), pn;
29536         if(pn = el.up('td.x-date-mp-month', 2)){
29537             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29538             this.hideMonthPicker();
29539         }
29540         else if(pn = el.up('td.x-date-mp-year', 2)){
29541             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29542             this.hideMonthPicker();
29543         }
29544     },
29545
29546     hideMonthPicker : function(disableAnim){
29547         if(this.monthPicker){
29548             if(disableAnim === true){
29549                 this.monthPicker.hide();
29550             }else{
29551                 this.monthPicker.slideOut('t', {duration:.2});
29552             }
29553         }
29554     },
29555
29556     // private
29557     showPrevMonth : function(e){
29558         this.update(this.activeDate.add("mo", -1));
29559     },
29560
29561     // private
29562     showNextMonth : function(e){
29563         this.update(this.activeDate.add("mo", 1));
29564     },
29565
29566     // private
29567     showPrevYear : function(){
29568         this.update(this.activeDate.add("y", -1));
29569     },
29570
29571     // private
29572     showNextYear : function(){
29573         this.update(this.activeDate.add("y", 1));
29574     },
29575
29576     // private
29577     handleMouseWheel : function(e){
29578         var delta = e.getWheelDelta();
29579         if(delta > 0){
29580             this.showPrevMonth();
29581             e.stopEvent();
29582         } else if(delta < 0){
29583             this.showNextMonth();
29584             e.stopEvent();
29585         }
29586     },
29587
29588     // private
29589     handleDateClick : function(e, t){
29590         e.stopEvent();
29591         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29592             this.setValue(new Date(t.dateValue));
29593             this.fireEvent("select", this, this.value);
29594         }
29595     },
29596
29597     // private
29598     selectToday : function(){
29599         this.setValue(new Date().clearTime());
29600         this.fireEvent("select", this, this.value);
29601     },
29602
29603     // private
29604     update : function(date)
29605     {
29606         var vd = this.activeDate;
29607         this.activeDate = date;
29608         if(vd && this.el){
29609             var t = date.getTime();
29610             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29611                 this.cells.removeClass("x-date-selected");
29612                 this.cells.each(function(c){
29613                    if(c.dom.firstChild.dateValue == t){
29614                        c.addClass("x-date-selected");
29615                        setTimeout(function(){
29616                             try{c.dom.firstChild.focus();}catch(e){}
29617                        }, 50);
29618                        return false;
29619                    }
29620                 });
29621                 return;
29622             }
29623         }
29624         
29625         var days = date.getDaysInMonth();
29626         var firstOfMonth = date.getFirstDateOfMonth();
29627         var startingPos = firstOfMonth.getDay()-this.startDay;
29628
29629         if(startingPos <= this.startDay){
29630             startingPos += 7;
29631         }
29632
29633         var pm = date.add("mo", -1);
29634         var prevStart = pm.getDaysInMonth()-startingPos;
29635
29636         var cells = this.cells.elements;
29637         var textEls = this.textNodes;
29638         days += startingPos;
29639
29640         // convert everything to numbers so it's fast
29641         var day = 86400000;
29642         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29643         var today = new Date().clearTime().getTime();
29644         var sel = date.clearTime().getTime();
29645         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29646         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29647         var ddMatch = this.disabledDatesRE;
29648         var ddText = this.disabledDatesText;
29649         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29650         var ddaysText = this.disabledDaysText;
29651         var format = this.format;
29652
29653         var setCellClass = function(cal, cell){
29654             cell.title = "";
29655             var t = d.getTime();
29656             cell.firstChild.dateValue = t;
29657             if(t == today){
29658                 cell.className += " x-date-today";
29659                 cell.title = cal.todayText;
29660             }
29661             if(t == sel){
29662                 cell.className += " x-date-selected";
29663                 setTimeout(function(){
29664                     try{cell.firstChild.focus();}catch(e){}
29665                 }, 50);
29666             }
29667             // disabling
29668             if(t < min) {
29669                 cell.className = " x-date-disabled";
29670                 cell.title = cal.minText;
29671                 return;
29672             }
29673             if(t > max) {
29674                 cell.className = " x-date-disabled";
29675                 cell.title = cal.maxText;
29676                 return;
29677             }
29678             if(ddays){
29679                 if(ddays.indexOf(d.getDay()) != -1){
29680                     cell.title = ddaysText;
29681                     cell.className = " x-date-disabled";
29682                 }
29683             }
29684             if(ddMatch && format){
29685                 var fvalue = d.dateFormat(format);
29686                 if(ddMatch.test(fvalue)){
29687                     cell.title = ddText.replace("%0", fvalue);
29688                     cell.className = " x-date-disabled";
29689                 }
29690             }
29691         };
29692
29693         var i = 0;
29694         for(; i < startingPos; i++) {
29695             textEls[i].innerHTML = (++prevStart);
29696             d.setDate(d.getDate()+1);
29697             cells[i].className = "x-date-prevday";
29698             setCellClass(this, cells[i]);
29699         }
29700         for(; i < days; i++){
29701             intDay = i - startingPos + 1;
29702             textEls[i].innerHTML = (intDay);
29703             d.setDate(d.getDate()+1);
29704             cells[i].className = "x-date-active";
29705             setCellClass(this, cells[i]);
29706         }
29707         var extraDays = 0;
29708         for(; i < 42; i++) {
29709              textEls[i].innerHTML = (++extraDays);
29710              d.setDate(d.getDate()+1);
29711              cells[i].className = "x-date-nextday";
29712              setCellClass(this, cells[i]);
29713         }
29714
29715         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29716         this.fireEvent('monthchange', this, date);
29717         
29718         if(!this.internalRender){
29719             var main = this.el.dom.firstChild;
29720             var w = main.offsetWidth;
29721             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29722             Roo.fly(main).setWidth(w);
29723             this.internalRender = true;
29724             // opera does not respect the auto grow header center column
29725             // then, after it gets a width opera refuses to recalculate
29726             // without a second pass
29727             if(Roo.isOpera && !this.secondPass){
29728                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29729                 this.secondPass = true;
29730                 this.update.defer(10, this, [date]);
29731             }
29732         }
29733         
29734         
29735     }
29736 });        /*
29737  * Based on:
29738  * Ext JS Library 1.1.1
29739  * Copyright(c) 2006-2007, Ext JS, LLC.
29740  *
29741  * Originally Released Under LGPL - original licence link has changed is not relivant.
29742  *
29743  * Fork - LGPL
29744  * <script type="text/javascript">
29745  */
29746 /**
29747  * @class Roo.TabPanel
29748  * @extends Roo.util.Observable
29749  * A lightweight tab container.
29750  * <br><br>
29751  * Usage:
29752  * <pre><code>
29753 // basic tabs 1, built from existing content
29754 var tabs = new Roo.TabPanel("tabs1");
29755 tabs.addTab("script", "View Script");
29756 tabs.addTab("markup", "View Markup");
29757 tabs.activate("script");
29758
29759 // more advanced tabs, built from javascript
29760 var jtabs = new Roo.TabPanel("jtabs");
29761 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29762
29763 // set up the UpdateManager
29764 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29765 var updater = tab2.getUpdateManager();
29766 updater.setDefaultUrl("ajax1.htm");
29767 tab2.on('activate', updater.refresh, updater, true);
29768
29769 // Use setUrl for Ajax loading
29770 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29771 tab3.setUrl("ajax2.htm", null, true);
29772
29773 // Disabled tab
29774 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29775 tab4.disable();
29776
29777 jtabs.activate("jtabs-1");
29778  * </code></pre>
29779  * @constructor
29780  * Create a new TabPanel.
29781  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29782  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29783  */
29784 Roo.TabPanel = function(container, config){
29785     /**
29786     * The container element for this TabPanel.
29787     * @type Roo.Element
29788     */
29789     this.el = Roo.get(container, true);
29790     if(config){
29791         if(typeof config == "boolean"){
29792             this.tabPosition = config ? "bottom" : "top";
29793         }else{
29794             Roo.apply(this, config);
29795         }
29796     }
29797     if(this.tabPosition == "bottom"){
29798         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29799         this.el.addClass("x-tabs-bottom");
29800     }
29801     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29802     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29803     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29804     if(Roo.isIE){
29805         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29806     }
29807     if(this.tabPosition != "bottom"){
29808         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29809          * @type Roo.Element
29810          */
29811         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29812         this.el.addClass("x-tabs-top");
29813     }
29814     this.items = [];
29815
29816     this.bodyEl.setStyle("position", "relative");
29817
29818     this.active = null;
29819     this.activateDelegate = this.activate.createDelegate(this);
29820
29821     this.addEvents({
29822         /**
29823          * @event tabchange
29824          * Fires when the active tab changes
29825          * @param {Roo.TabPanel} this
29826          * @param {Roo.TabPanelItem} activePanel The new active tab
29827          */
29828         "tabchange": true,
29829         /**
29830          * @event beforetabchange
29831          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29832          * @param {Roo.TabPanel} this
29833          * @param {Object} e Set cancel to true on this object to cancel the tab change
29834          * @param {Roo.TabPanelItem} tab The tab being changed to
29835          */
29836         "beforetabchange" : true
29837     });
29838
29839     Roo.EventManager.onWindowResize(this.onResize, this);
29840     this.cpad = this.el.getPadding("lr");
29841     this.hiddenCount = 0;
29842
29843
29844     // toolbar on the tabbar support...
29845     if (this.toolbar) {
29846         var tcfg = this.toolbar;
29847         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29848         this.toolbar = new Roo.Toolbar(tcfg);
29849         if (Roo.isSafari) {
29850             var tbl = tcfg.container.child('table', true);
29851             tbl.setAttribute('width', '100%');
29852         }
29853         
29854     }
29855    
29856
29857
29858     Roo.TabPanel.superclass.constructor.call(this);
29859 };
29860
29861 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29862     /*
29863      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29864      */
29865     tabPosition : "top",
29866     /*
29867      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29868      */
29869     currentTabWidth : 0,
29870     /*
29871      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29872      */
29873     minTabWidth : 40,
29874     /*
29875      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29876      */
29877     maxTabWidth : 250,
29878     /*
29879      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29880      */
29881     preferredTabWidth : 175,
29882     /*
29883      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29884      */
29885     resizeTabs : false,
29886     /*
29887      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29888      */
29889     monitorResize : true,
29890     /*
29891      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29892      */
29893     toolbar : false,
29894
29895     /**
29896      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29897      * @param {String} id The id of the div to use <b>or create</b>
29898      * @param {String} text The text for the tab
29899      * @param {String} content (optional) Content to put in the TabPanelItem body
29900      * @param {Boolean} closable (optional) True to create a close icon on the tab
29901      * @return {Roo.TabPanelItem} The created TabPanelItem
29902      */
29903     addTab : function(id, text, content, closable){
29904         var item = new Roo.TabPanelItem(this, id, text, closable);
29905         this.addTabItem(item);
29906         if(content){
29907             item.setContent(content);
29908         }
29909         return item;
29910     },
29911
29912     /**
29913      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29914      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29915      * @return {Roo.TabPanelItem}
29916      */
29917     getTab : function(id){
29918         return this.items[id];
29919     },
29920
29921     /**
29922      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29923      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29924      */
29925     hideTab : function(id){
29926         var t = this.items[id];
29927         if(!t.isHidden()){
29928            t.setHidden(true);
29929            this.hiddenCount++;
29930            this.autoSizeTabs();
29931         }
29932     },
29933
29934     /**
29935      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29936      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29937      */
29938     unhideTab : function(id){
29939         var t = this.items[id];
29940         if(t.isHidden()){
29941            t.setHidden(false);
29942            this.hiddenCount--;
29943            this.autoSizeTabs();
29944         }
29945     },
29946
29947     /**
29948      * Adds an existing {@link Roo.TabPanelItem}.
29949      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29950      */
29951     addTabItem : function(item){
29952         this.items[item.id] = item;
29953         this.items.push(item);
29954         if(this.resizeTabs){
29955            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29956            this.autoSizeTabs();
29957         }else{
29958             item.autoSize();
29959         }
29960     },
29961
29962     /**
29963      * Removes a {@link Roo.TabPanelItem}.
29964      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29965      */
29966     removeTab : function(id){
29967         var items = this.items;
29968         var tab = items[id];
29969         if(!tab) { return; }
29970         var index = items.indexOf(tab);
29971         if(this.active == tab && items.length > 1){
29972             var newTab = this.getNextAvailable(index);
29973             if(newTab) {
29974                 newTab.activate();
29975             }
29976         }
29977         this.stripEl.dom.removeChild(tab.pnode.dom);
29978         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29979             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29980         }
29981         items.splice(index, 1);
29982         delete this.items[tab.id];
29983         tab.fireEvent("close", tab);
29984         tab.purgeListeners();
29985         this.autoSizeTabs();
29986     },
29987
29988     getNextAvailable : function(start){
29989         var items = this.items;
29990         var index = start;
29991         // look for a next tab that will slide over to
29992         // replace the one being removed
29993         while(index < items.length){
29994             var item = items[++index];
29995             if(item && !item.isHidden()){
29996                 return item;
29997             }
29998         }
29999         // if one isn't found select the previous tab (on the left)
30000         index = start;
30001         while(index >= 0){
30002             var item = items[--index];
30003             if(item && !item.isHidden()){
30004                 return item;
30005             }
30006         }
30007         return null;
30008     },
30009
30010     /**
30011      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30012      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30013      */
30014     disableTab : function(id){
30015         var tab = this.items[id];
30016         if(tab && this.active != tab){
30017             tab.disable();
30018         }
30019     },
30020
30021     /**
30022      * Enables a {@link Roo.TabPanelItem} that is disabled.
30023      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30024      */
30025     enableTab : function(id){
30026         var tab = this.items[id];
30027         tab.enable();
30028     },
30029
30030     /**
30031      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30032      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30033      * @return {Roo.TabPanelItem} The TabPanelItem.
30034      */
30035     activate : function(id){
30036         var tab = this.items[id];
30037         if(!tab){
30038             return null;
30039         }
30040         if(tab == this.active || tab.disabled){
30041             return tab;
30042         }
30043         var e = {};
30044         this.fireEvent("beforetabchange", this, e, tab);
30045         if(e.cancel !== true && !tab.disabled){
30046             if(this.active){
30047                 this.active.hide();
30048             }
30049             this.active = this.items[id];
30050             this.active.show();
30051             this.fireEvent("tabchange", this, this.active);
30052         }
30053         return tab;
30054     },
30055
30056     /**
30057      * Gets the active {@link Roo.TabPanelItem}.
30058      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30059      */
30060     getActiveTab : function(){
30061         return this.active;
30062     },
30063
30064     /**
30065      * Updates the tab body element to fit the height of the container element
30066      * for overflow scrolling
30067      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30068      */
30069     syncHeight : function(targetHeight){
30070         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30071         var bm = this.bodyEl.getMargins();
30072         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30073         this.bodyEl.setHeight(newHeight);
30074         return newHeight;
30075     },
30076
30077     onResize : function(){
30078         if(this.monitorResize){
30079             this.autoSizeTabs();
30080         }
30081     },
30082
30083     /**
30084      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30085      */
30086     beginUpdate : function(){
30087         this.updating = true;
30088     },
30089
30090     /**
30091      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30092      */
30093     endUpdate : function(){
30094         this.updating = false;
30095         this.autoSizeTabs();
30096     },
30097
30098     /**
30099      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30100      */
30101     autoSizeTabs : function(){
30102         var count = this.items.length;
30103         var vcount = count - this.hiddenCount;
30104         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30105             return;
30106         }
30107         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30108         var availWidth = Math.floor(w / vcount);
30109         var b = this.stripBody;
30110         if(b.getWidth() > w){
30111             var tabs = this.items;
30112             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30113             if(availWidth < this.minTabWidth){
30114                 /*if(!this.sleft){    // incomplete scrolling code
30115                     this.createScrollButtons();
30116                 }
30117                 this.showScroll();
30118                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30119             }
30120         }else{
30121             if(this.currentTabWidth < this.preferredTabWidth){
30122                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30123             }
30124         }
30125     },
30126
30127     /**
30128      * Returns the number of tabs in this TabPanel.
30129      * @return {Number}
30130      */
30131      getCount : function(){
30132          return this.items.length;
30133      },
30134
30135     /**
30136      * Resizes all the tabs to the passed width
30137      * @param {Number} The new width
30138      */
30139     setTabWidth : function(width){
30140         this.currentTabWidth = width;
30141         for(var i = 0, len = this.items.length; i < len; i++) {
30142                 if(!this.items[i].isHidden()) {
30143                 this.items[i].setWidth(width);
30144             }
30145         }
30146     },
30147
30148     /**
30149      * Destroys this TabPanel
30150      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30151      */
30152     destroy : function(removeEl){
30153         Roo.EventManager.removeResizeListener(this.onResize, this);
30154         for(var i = 0, len = this.items.length; i < len; i++){
30155             this.items[i].purgeListeners();
30156         }
30157         if(removeEl === true){
30158             this.el.update("");
30159             this.el.remove();
30160         }
30161     }
30162 });
30163
30164 /**
30165  * @class Roo.TabPanelItem
30166  * @extends Roo.util.Observable
30167  * Represents an individual item (tab plus body) in a TabPanel.
30168  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30169  * @param {String} id The id of this TabPanelItem
30170  * @param {String} text The text for the tab of this TabPanelItem
30171  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30172  */
30173 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30174     /**
30175      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30176      * @type Roo.TabPanel
30177      */
30178     this.tabPanel = tabPanel;
30179     /**
30180      * The id for this TabPanelItem
30181      * @type String
30182      */
30183     this.id = id;
30184     /** @private */
30185     this.disabled = false;
30186     /** @private */
30187     this.text = text;
30188     /** @private */
30189     this.loaded = false;
30190     this.closable = closable;
30191
30192     /**
30193      * The body element for this TabPanelItem.
30194      * @type Roo.Element
30195      */
30196     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30197     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30198     this.bodyEl.setStyle("display", "block");
30199     this.bodyEl.setStyle("zoom", "1");
30200     this.hideAction();
30201
30202     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30203     /** @private */
30204     this.el = Roo.get(els.el, true);
30205     this.inner = Roo.get(els.inner, true);
30206     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30207     this.pnode = Roo.get(els.el.parentNode, true);
30208     this.el.on("mousedown", this.onTabMouseDown, this);
30209     this.el.on("click", this.onTabClick, this);
30210     /** @private */
30211     if(closable){
30212         var c = Roo.get(els.close, true);
30213         c.dom.title = this.closeText;
30214         c.addClassOnOver("close-over");
30215         c.on("click", this.closeClick, this);
30216      }
30217
30218     this.addEvents({
30219          /**
30220          * @event activate
30221          * Fires when this tab becomes the active tab.
30222          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30223          * @param {Roo.TabPanelItem} this
30224          */
30225         "activate": true,
30226         /**
30227          * @event beforeclose
30228          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30229          * @param {Roo.TabPanelItem} this
30230          * @param {Object} e Set cancel to true on this object to cancel the close.
30231          */
30232         "beforeclose": true,
30233         /**
30234          * @event close
30235          * Fires when this tab is closed.
30236          * @param {Roo.TabPanelItem} this
30237          */
30238          "close": true,
30239         /**
30240          * @event deactivate
30241          * Fires when this tab is no longer the active tab.
30242          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30243          * @param {Roo.TabPanelItem} this
30244          */
30245          "deactivate" : true
30246     });
30247     this.hidden = false;
30248
30249     Roo.TabPanelItem.superclass.constructor.call(this);
30250 };
30251
30252 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30253     purgeListeners : function(){
30254        Roo.util.Observable.prototype.purgeListeners.call(this);
30255        this.el.removeAllListeners();
30256     },
30257     /**
30258      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30259      */
30260     show : function(){
30261         this.pnode.addClass("on");
30262         this.showAction();
30263         if(Roo.isOpera){
30264             this.tabPanel.stripWrap.repaint();
30265         }
30266         this.fireEvent("activate", this.tabPanel, this);
30267     },
30268
30269     /**
30270      * Returns true if this tab is the active tab.
30271      * @return {Boolean}
30272      */
30273     isActive : function(){
30274         return this.tabPanel.getActiveTab() == this;
30275     },
30276
30277     /**
30278      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30279      */
30280     hide : function(){
30281         this.pnode.removeClass("on");
30282         this.hideAction();
30283         this.fireEvent("deactivate", this.tabPanel, this);
30284     },
30285
30286     hideAction : function(){
30287         this.bodyEl.hide();
30288         this.bodyEl.setStyle("position", "absolute");
30289         this.bodyEl.setLeft("-20000px");
30290         this.bodyEl.setTop("-20000px");
30291     },
30292
30293     showAction : function(){
30294         this.bodyEl.setStyle("position", "relative");
30295         this.bodyEl.setTop("");
30296         this.bodyEl.setLeft("");
30297         this.bodyEl.show();
30298     },
30299
30300     /**
30301      * Set the tooltip for the tab.
30302      * @param {String} tooltip The tab's tooltip
30303      */
30304     setTooltip : function(text){
30305         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30306             this.textEl.dom.qtip = text;
30307             this.textEl.dom.removeAttribute('title');
30308         }else{
30309             this.textEl.dom.title = text;
30310         }
30311     },
30312
30313     onTabClick : function(e){
30314         e.preventDefault();
30315         this.tabPanel.activate(this.id);
30316     },
30317
30318     onTabMouseDown : function(e){
30319         e.preventDefault();
30320         this.tabPanel.activate(this.id);
30321     },
30322
30323     getWidth : function(){
30324         return this.inner.getWidth();
30325     },
30326
30327     setWidth : function(width){
30328         var iwidth = width - this.pnode.getPadding("lr");
30329         this.inner.setWidth(iwidth);
30330         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30331         this.pnode.setWidth(width);
30332     },
30333
30334     /**
30335      * Show or hide the tab
30336      * @param {Boolean} hidden True to hide or false to show.
30337      */
30338     setHidden : function(hidden){
30339         this.hidden = hidden;
30340         this.pnode.setStyle("display", hidden ? "none" : "");
30341     },
30342
30343     /**
30344      * Returns true if this tab is "hidden"
30345      * @return {Boolean}
30346      */
30347     isHidden : function(){
30348         return this.hidden;
30349     },
30350
30351     /**
30352      * Returns the text for this tab
30353      * @return {String}
30354      */
30355     getText : function(){
30356         return this.text;
30357     },
30358
30359     autoSize : function(){
30360         //this.el.beginMeasure();
30361         this.textEl.setWidth(1);
30362         /*
30363          *  #2804 [new] Tabs in Roojs
30364          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30365          */
30366         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30367         //this.el.endMeasure();
30368     },
30369
30370     /**
30371      * Sets the text for the tab (Note: this also sets the tooltip text)
30372      * @param {String} text The tab's text and tooltip
30373      */
30374     setText : function(text){
30375         this.text = text;
30376         this.textEl.update(text);
30377         this.setTooltip(text);
30378         if(!this.tabPanel.resizeTabs){
30379             this.autoSize();
30380         }
30381     },
30382     /**
30383      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30384      */
30385     activate : function(){
30386         this.tabPanel.activate(this.id);
30387     },
30388
30389     /**
30390      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30391      */
30392     disable : function(){
30393         if(this.tabPanel.active != this){
30394             this.disabled = true;
30395             this.pnode.addClass("disabled");
30396         }
30397     },
30398
30399     /**
30400      * Enables this TabPanelItem if it was previously disabled.
30401      */
30402     enable : function(){
30403         this.disabled = false;
30404         this.pnode.removeClass("disabled");
30405     },
30406
30407     /**
30408      * Sets the content for this TabPanelItem.
30409      * @param {String} content The content
30410      * @param {Boolean} loadScripts true to look for and load scripts
30411      */
30412     setContent : function(content, loadScripts){
30413         this.bodyEl.update(content, loadScripts);
30414     },
30415
30416     /**
30417      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30418      * @return {Roo.UpdateManager} The UpdateManager
30419      */
30420     getUpdateManager : function(){
30421         return this.bodyEl.getUpdateManager();
30422     },
30423
30424     /**
30425      * Set a URL to be used to load the content for this TabPanelItem.
30426      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30427      * @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)
30428      * @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)
30429      * @return {Roo.UpdateManager} The UpdateManager
30430      */
30431     setUrl : function(url, params, loadOnce){
30432         if(this.refreshDelegate){
30433             this.un('activate', this.refreshDelegate);
30434         }
30435         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30436         this.on("activate", this.refreshDelegate);
30437         return this.bodyEl.getUpdateManager();
30438     },
30439
30440     /** @private */
30441     _handleRefresh : function(url, params, loadOnce){
30442         if(!loadOnce || !this.loaded){
30443             var updater = this.bodyEl.getUpdateManager();
30444             updater.update(url, params, this._setLoaded.createDelegate(this));
30445         }
30446     },
30447
30448     /**
30449      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30450      *   Will fail silently if the setUrl method has not been called.
30451      *   This does not activate the panel, just updates its content.
30452      */
30453     refresh : function(){
30454         if(this.refreshDelegate){
30455            this.loaded = false;
30456            this.refreshDelegate();
30457         }
30458     },
30459
30460     /** @private */
30461     _setLoaded : function(){
30462         this.loaded = true;
30463     },
30464
30465     /** @private */
30466     closeClick : function(e){
30467         var o = {};
30468         e.stopEvent();
30469         this.fireEvent("beforeclose", this, o);
30470         if(o.cancel !== true){
30471             this.tabPanel.removeTab(this.id);
30472         }
30473     },
30474     /**
30475      * The text displayed in the tooltip for the close icon.
30476      * @type String
30477      */
30478     closeText : "Close this tab"
30479 });
30480
30481 /** @private */
30482 Roo.TabPanel.prototype.createStrip = function(container){
30483     var strip = document.createElement("div");
30484     strip.className = "x-tabs-wrap";
30485     container.appendChild(strip);
30486     return strip;
30487 };
30488 /** @private */
30489 Roo.TabPanel.prototype.createStripList = function(strip){
30490     // div wrapper for retard IE
30491     // returns the "tr" element.
30492     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30493         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30494         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30495     return strip.firstChild.firstChild.firstChild.firstChild;
30496 };
30497 /** @private */
30498 Roo.TabPanel.prototype.createBody = function(container){
30499     var body = document.createElement("div");
30500     Roo.id(body, "tab-body");
30501     Roo.fly(body).addClass("x-tabs-body");
30502     container.appendChild(body);
30503     return body;
30504 };
30505 /** @private */
30506 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30507     var body = Roo.getDom(id);
30508     if(!body){
30509         body = document.createElement("div");
30510         body.id = id;
30511     }
30512     Roo.fly(body).addClass("x-tabs-item-body");
30513     bodyEl.insertBefore(body, bodyEl.firstChild);
30514     return body;
30515 };
30516 /** @private */
30517 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30518     var td = document.createElement("td");
30519     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30520     //stripEl.appendChild(td);
30521     if(closable){
30522         td.className = "x-tabs-closable";
30523         if(!this.closeTpl){
30524             this.closeTpl = new Roo.Template(
30525                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30526                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30527                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30528             );
30529         }
30530         var el = this.closeTpl.overwrite(td, {"text": text});
30531         var close = el.getElementsByTagName("div")[0];
30532         var inner = el.getElementsByTagName("em")[0];
30533         return {"el": el, "close": close, "inner": inner};
30534     } else {
30535         if(!this.tabTpl){
30536             this.tabTpl = new Roo.Template(
30537                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30538                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30539             );
30540         }
30541         var el = this.tabTpl.overwrite(td, {"text": text});
30542         var inner = el.getElementsByTagName("em")[0];
30543         return {"el": el, "inner": inner};
30544     }
30545 };/*
30546  * Based on:
30547  * Ext JS Library 1.1.1
30548  * Copyright(c) 2006-2007, Ext JS, LLC.
30549  *
30550  * Originally Released Under LGPL - original licence link has changed is not relivant.
30551  *
30552  * Fork - LGPL
30553  * <script type="text/javascript">
30554  */
30555
30556 /**
30557  * @class Roo.Button
30558  * @extends Roo.util.Observable
30559  * Simple Button class
30560  * @cfg {String} text The button text
30561  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30562  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30563  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30564  * @cfg {Object} scope The scope of the handler
30565  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30566  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30567  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30568  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30569  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30570  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30571    applies if enableToggle = true)
30572  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30573  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30574   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30575  * @constructor
30576  * Create a new button
30577  * @param {Object} config The config object
30578  */
30579 Roo.Button = function(renderTo, config)
30580 {
30581     if (!config) {
30582         config = renderTo;
30583         renderTo = config.renderTo || false;
30584     }
30585     
30586     Roo.apply(this, config);
30587     this.addEvents({
30588         /**
30589              * @event click
30590              * Fires when this button is clicked
30591              * @param {Button} this
30592              * @param {EventObject} e The click event
30593              */
30594             "click" : true,
30595         /**
30596              * @event toggle
30597              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30598              * @param {Button} this
30599              * @param {Boolean} pressed
30600              */
30601             "toggle" : true,
30602         /**
30603              * @event mouseover
30604              * Fires when the mouse hovers over the button
30605              * @param {Button} this
30606              * @param {Event} e The event object
30607              */
30608         'mouseover' : true,
30609         /**
30610              * @event mouseout
30611              * Fires when the mouse exits the button
30612              * @param {Button} this
30613              * @param {Event} e The event object
30614              */
30615         'mouseout': true,
30616          /**
30617              * @event render
30618              * Fires when the button is rendered
30619              * @param {Button} this
30620              */
30621         'render': true
30622     });
30623     if(this.menu){
30624         this.menu = Roo.menu.MenuMgr.get(this.menu);
30625     }
30626     // register listeners first!!  - so render can be captured..
30627     Roo.util.Observable.call(this);
30628     if(renderTo){
30629         this.render(renderTo);
30630     }
30631     
30632   
30633 };
30634
30635 Roo.extend(Roo.Button, Roo.util.Observable, {
30636     /**
30637      * 
30638      */
30639     
30640     /**
30641      * Read-only. True if this button is hidden
30642      * @type Boolean
30643      */
30644     hidden : false,
30645     /**
30646      * Read-only. True if this button is disabled
30647      * @type Boolean
30648      */
30649     disabled : false,
30650     /**
30651      * Read-only. True if this button is pressed (only if enableToggle = true)
30652      * @type Boolean
30653      */
30654     pressed : false,
30655
30656     /**
30657      * @cfg {Number} tabIndex 
30658      * The DOM tabIndex for this button (defaults to undefined)
30659      */
30660     tabIndex : undefined,
30661
30662     /**
30663      * @cfg {Boolean} enableToggle
30664      * True to enable pressed/not pressed toggling (defaults to false)
30665      */
30666     enableToggle: false,
30667     /**
30668      * @cfg {Roo.menu.Menu} menu
30669      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30670      */
30671     menu : undefined,
30672     /**
30673      * @cfg {String} menuAlign
30674      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30675      */
30676     menuAlign : "tl-bl?",
30677
30678     /**
30679      * @cfg {String} iconCls
30680      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30681      */
30682     iconCls : undefined,
30683     /**
30684      * @cfg {String} type
30685      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30686      */
30687     type : 'button',
30688
30689     // private
30690     menuClassTarget: 'tr',
30691
30692     /**
30693      * @cfg {String} clickEvent
30694      * The type of event to map to the button's event handler (defaults to 'click')
30695      */
30696     clickEvent : 'click',
30697
30698     /**
30699      * @cfg {Boolean} handleMouseEvents
30700      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30701      */
30702     handleMouseEvents : true,
30703
30704     /**
30705      * @cfg {String} tooltipType
30706      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30707      */
30708     tooltipType : 'qtip',
30709
30710     /**
30711      * @cfg {String} cls
30712      * A CSS class to apply to the button's main element.
30713      */
30714     
30715     /**
30716      * @cfg {Roo.Template} template (Optional)
30717      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30718      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30719      * require code modifications if required elements (e.g. a button) aren't present.
30720      */
30721
30722     // private
30723     render : function(renderTo){
30724         var btn;
30725         if(this.hideParent){
30726             this.parentEl = Roo.get(renderTo);
30727         }
30728         if(!this.dhconfig){
30729             if(!this.template){
30730                 if(!Roo.Button.buttonTemplate){
30731                     // hideous table template
30732                     Roo.Button.buttonTemplate = new Roo.Template(
30733                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30734                         '<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>',
30735                         "</tr></tbody></table>");
30736                 }
30737                 this.template = Roo.Button.buttonTemplate;
30738             }
30739             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30740             var btnEl = btn.child("button:first");
30741             btnEl.on('focus', this.onFocus, this);
30742             btnEl.on('blur', this.onBlur, this);
30743             if(this.cls){
30744                 btn.addClass(this.cls);
30745             }
30746             if(this.icon){
30747                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30748             }
30749             if(this.iconCls){
30750                 btnEl.addClass(this.iconCls);
30751                 if(!this.cls){
30752                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30753                 }
30754             }
30755             if(this.tabIndex !== undefined){
30756                 btnEl.dom.tabIndex = this.tabIndex;
30757             }
30758             if(this.tooltip){
30759                 if(typeof this.tooltip == 'object'){
30760                     Roo.QuickTips.tips(Roo.apply({
30761                           target: btnEl.id
30762                     }, this.tooltip));
30763                 } else {
30764                     btnEl.dom[this.tooltipType] = this.tooltip;
30765                 }
30766             }
30767         }else{
30768             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30769         }
30770         this.el = btn;
30771         if(this.id){
30772             this.el.dom.id = this.el.id = this.id;
30773         }
30774         if(this.menu){
30775             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30776             this.menu.on("show", this.onMenuShow, this);
30777             this.menu.on("hide", this.onMenuHide, this);
30778         }
30779         btn.addClass("x-btn");
30780         if(Roo.isIE && !Roo.isIE7){
30781             this.autoWidth.defer(1, this);
30782         }else{
30783             this.autoWidth();
30784         }
30785         if(this.handleMouseEvents){
30786             btn.on("mouseover", this.onMouseOver, this);
30787             btn.on("mouseout", this.onMouseOut, this);
30788             btn.on("mousedown", this.onMouseDown, this);
30789         }
30790         btn.on(this.clickEvent, this.onClick, this);
30791         //btn.on("mouseup", this.onMouseUp, this);
30792         if(this.hidden){
30793             this.hide();
30794         }
30795         if(this.disabled){
30796             this.disable();
30797         }
30798         Roo.ButtonToggleMgr.register(this);
30799         if(this.pressed){
30800             this.el.addClass("x-btn-pressed");
30801         }
30802         if(this.repeat){
30803             var repeater = new Roo.util.ClickRepeater(btn,
30804                 typeof this.repeat == "object" ? this.repeat : {}
30805             );
30806             repeater.on("click", this.onClick,  this);
30807         }
30808         
30809         this.fireEvent('render', this);
30810         
30811     },
30812     /**
30813      * Returns the button's underlying element
30814      * @return {Roo.Element} The element
30815      */
30816     getEl : function(){
30817         return this.el;  
30818     },
30819     
30820     /**
30821      * Destroys this Button and removes any listeners.
30822      */
30823     destroy : function(){
30824         Roo.ButtonToggleMgr.unregister(this);
30825         this.el.removeAllListeners();
30826         this.purgeListeners();
30827         this.el.remove();
30828     },
30829
30830     // private
30831     autoWidth : function(){
30832         if(this.el){
30833             this.el.setWidth("auto");
30834             if(Roo.isIE7 && Roo.isStrict){
30835                 var ib = this.el.child('button');
30836                 if(ib && ib.getWidth() > 20){
30837                     ib.clip();
30838                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30839                 }
30840             }
30841             if(this.minWidth){
30842                 if(this.hidden){
30843                     this.el.beginMeasure();
30844                 }
30845                 if(this.el.getWidth() < this.minWidth){
30846                     this.el.setWidth(this.minWidth);
30847                 }
30848                 if(this.hidden){
30849                     this.el.endMeasure();
30850                 }
30851             }
30852         }
30853     },
30854
30855     /**
30856      * Assigns this button's click handler
30857      * @param {Function} handler The function to call when the button is clicked
30858      * @param {Object} scope (optional) Scope for the function passed in
30859      */
30860     setHandler : function(handler, scope){
30861         this.handler = handler;
30862         this.scope = scope;  
30863     },
30864     
30865     /**
30866      * Sets this button's text
30867      * @param {String} text The button text
30868      */
30869     setText : function(text){
30870         this.text = text;
30871         if(this.el){
30872             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30873         }
30874         this.autoWidth();
30875     },
30876     
30877     /**
30878      * Gets the text for this button
30879      * @return {String} The button text
30880      */
30881     getText : function(){
30882         return this.text;  
30883     },
30884     
30885     /**
30886      * Show this button
30887      */
30888     show: function(){
30889         this.hidden = false;
30890         if(this.el){
30891             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30892         }
30893     },
30894     
30895     /**
30896      * Hide this button
30897      */
30898     hide: function(){
30899         this.hidden = true;
30900         if(this.el){
30901             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30902         }
30903     },
30904     
30905     /**
30906      * Convenience function for boolean show/hide
30907      * @param {Boolean} visible True to show, false to hide
30908      */
30909     setVisible: function(visible){
30910         if(visible) {
30911             this.show();
30912         }else{
30913             this.hide();
30914         }
30915     },
30916     
30917     /**
30918      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30919      * @param {Boolean} state (optional) Force a particular state
30920      */
30921     toggle : function(state){
30922         state = state === undefined ? !this.pressed : state;
30923         if(state != this.pressed){
30924             if(state){
30925                 this.el.addClass("x-btn-pressed");
30926                 this.pressed = true;
30927                 this.fireEvent("toggle", this, true);
30928             }else{
30929                 this.el.removeClass("x-btn-pressed");
30930                 this.pressed = false;
30931                 this.fireEvent("toggle", this, false);
30932             }
30933             if(this.toggleHandler){
30934                 this.toggleHandler.call(this.scope || this, this, state);
30935             }
30936         }
30937     },
30938     
30939     /**
30940      * Focus the button
30941      */
30942     focus : function(){
30943         this.el.child('button:first').focus();
30944     },
30945     
30946     /**
30947      * Disable this button
30948      */
30949     disable : function(){
30950         if(this.el){
30951             this.el.addClass("x-btn-disabled");
30952         }
30953         this.disabled = true;
30954     },
30955     
30956     /**
30957      * Enable this button
30958      */
30959     enable : function(){
30960         if(this.el){
30961             this.el.removeClass("x-btn-disabled");
30962         }
30963         this.disabled = false;
30964     },
30965
30966     /**
30967      * Convenience function for boolean enable/disable
30968      * @param {Boolean} enabled True to enable, false to disable
30969      */
30970     setDisabled : function(v){
30971         this[v !== true ? "enable" : "disable"]();
30972     },
30973
30974     // private
30975     onClick : function(e)
30976     {
30977         if(e){
30978             e.preventDefault();
30979         }
30980         if(e.button != 0){
30981             return;
30982         }
30983         if(!this.disabled){
30984             if(this.enableToggle){
30985                 this.toggle();
30986             }
30987             if(this.menu && !this.menu.isVisible()){
30988                 this.menu.show(this.el, this.menuAlign);
30989             }
30990             this.fireEvent("click", this, e);
30991             if(this.handler){
30992                 this.el.removeClass("x-btn-over");
30993                 this.handler.call(this.scope || this, this, e);
30994             }
30995         }
30996     },
30997     // private
30998     onMouseOver : function(e){
30999         if(!this.disabled){
31000             this.el.addClass("x-btn-over");
31001             this.fireEvent('mouseover', this, e);
31002         }
31003     },
31004     // private
31005     onMouseOut : function(e){
31006         if(!e.within(this.el,  true)){
31007             this.el.removeClass("x-btn-over");
31008             this.fireEvent('mouseout', this, e);
31009         }
31010     },
31011     // private
31012     onFocus : function(e){
31013         if(!this.disabled){
31014             this.el.addClass("x-btn-focus");
31015         }
31016     },
31017     // private
31018     onBlur : function(e){
31019         this.el.removeClass("x-btn-focus");
31020     },
31021     // private
31022     onMouseDown : function(e){
31023         if(!this.disabled && e.button == 0){
31024             this.el.addClass("x-btn-click");
31025             Roo.get(document).on('mouseup', this.onMouseUp, this);
31026         }
31027     },
31028     // private
31029     onMouseUp : function(e){
31030         if(e.button == 0){
31031             this.el.removeClass("x-btn-click");
31032             Roo.get(document).un('mouseup', this.onMouseUp, this);
31033         }
31034     },
31035     // private
31036     onMenuShow : function(e){
31037         this.el.addClass("x-btn-menu-active");
31038     },
31039     // private
31040     onMenuHide : function(e){
31041         this.el.removeClass("x-btn-menu-active");
31042     }   
31043 });
31044
31045 // Private utility class used by Button
31046 Roo.ButtonToggleMgr = function(){
31047    var groups = {};
31048    
31049    function toggleGroup(btn, state){
31050        if(state){
31051            var g = groups[btn.toggleGroup];
31052            for(var i = 0, l = g.length; i < l; i++){
31053                if(g[i] != btn){
31054                    g[i].toggle(false);
31055                }
31056            }
31057        }
31058    }
31059    
31060    return {
31061        register : function(btn){
31062            if(!btn.toggleGroup){
31063                return;
31064            }
31065            var g = groups[btn.toggleGroup];
31066            if(!g){
31067                g = groups[btn.toggleGroup] = [];
31068            }
31069            g.push(btn);
31070            btn.on("toggle", toggleGroup);
31071        },
31072        
31073        unregister : function(btn){
31074            if(!btn.toggleGroup){
31075                return;
31076            }
31077            var g = groups[btn.toggleGroup];
31078            if(g){
31079                g.remove(btn);
31080                btn.un("toggle", toggleGroup);
31081            }
31082        }
31083    };
31084 }();/*
31085  * Based on:
31086  * Ext JS Library 1.1.1
31087  * Copyright(c) 2006-2007, Ext JS, LLC.
31088  *
31089  * Originally Released Under LGPL - original licence link has changed is not relivant.
31090  *
31091  * Fork - LGPL
31092  * <script type="text/javascript">
31093  */
31094  
31095 /**
31096  * @class Roo.SplitButton
31097  * @extends Roo.Button
31098  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31099  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31100  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31101  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31102  * @cfg {String} arrowTooltip The title attribute of the arrow
31103  * @constructor
31104  * Create a new menu button
31105  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31106  * @param {Object} config The config object
31107  */
31108 Roo.SplitButton = function(renderTo, config){
31109     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31110     /**
31111      * @event arrowclick
31112      * Fires when this button's arrow is clicked
31113      * @param {SplitButton} this
31114      * @param {EventObject} e The click event
31115      */
31116     this.addEvents({"arrowclick":true});
31117 };
31118
31119 Roo.extend(Roo.SplitButton, Roo.Button, {
31120     render : function(renderTo){
31121         // this is one sweet looking template!
31122         var tpl = new Roo.Template(
31123             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31124             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31125             '<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>',
31126             "</tbody></table></td><td>",
31127             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31128             '<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>',
31129             "</tbody></table></td></tr></table>"
31130         );
31131         var btn = tpl.append(renderTo, [this.text, this.type], true);
31132         var btnEl = btn.child("button");
31133         if(this.cls){
31134             btn.addClass(this.cls);
31135         }
31136         if(this.icon){
31137             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31138         }
31139         if(this.iconCls){
31140             btnEl.addClass(this.iconCls);
31141             if(!this.cls){
31142                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31143             }
31144         }
31145         this.el = btn;
31146         if(this.handleMouseEvents){
31147             btn.on("mouseover", this.onMouseOver, this);
31148             btn.on("mouseout", this.onMouseOut, this);
31149             btn.on("mousedown", this.onMouseDown, this);
31150             btn.on("mouseup", this.onMouseUp, this);
31151         }
31152         btn.on(this.clickEvent, this.onClick, this);
31153         if(this.tooltip){
31154             if(typeof this.tooltip == 'object'){
31155                 Roo.QuickTips.tips(Roo.apply({
31156                       target: btnEl.id
31157                 }, this.tooltip));
31158             } else {
31159                 btnEl.dom[this.tooltipType] = this.tooltip;
31160             }
31161         }
31162         if(this.arrowTooltip){
31163             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31164         }
31165         if(this.hidden){
31166             this.hide();
31167         }
31168         if(this.disabled){
31169             this.disable();
31170         }
31171         if(this.pressed){
31172             this.el.addClass("x-btn-pressed");
31173         }
31174         if(Roo.isIE && !Roo.isIE7){
31175             this.autoWidth.defer(1, this);
31176         }else{
31177             this.autoWidth();
31178         }
31179         if(this.menu){
31180             this.menu.on("show", this.onMenuShow, this);
31181             this.menu.on("hide", this.onMenuHide, this);
31182         }
31183         this.fireEvent('render', this);
31184     },
31185
31186     // private
31187     autoWidth : function(){
31188         if(this.el){
31189             var tbl = this.el.child("table:first");
31190             var tbl2 = this.el.child("table:last");
31191             this.el.setWidth("auto");
31192             tbl.setWidth("auto");
31193             if(Roo.isIE7 && Roo.isStrict){
31194                 var ib = this.el.child('button:first');
31195                 if(ib && ib.getWidth() > 20){
31196                     ib.clip();
31197                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31198                 }
31199             }
31200             if(this.minWidth){
31201                 if(this.hidden){
31202                     this.el.beginMeasure();
31203                 }
31204                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31205                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31206                 }
31207                 if(this.hidden){
31208                     this.el.endMeasure();
31209                 }
31210             }
31211             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31212         } 
31213     },
31214     /**
31215      * Sets this button's click handler
31216      * @param {Function} handler The function to call when the button is clicked
31217      * @param {Object} scope (optional) Scope for the function passed above
31218      */
31219     setHandler : function(handler, scope){
31220         this.handler = handler;
31221         this.scope = scope;  
31222     },
31223     
31224     /**
31225      * Sets this button's arrow click handler
31226      * @param {Function} handler The function to call when the arrow is clicked
31227      * @param {Object} scope (optional) Scope for the function passed above
31228      */
31229     setArrowHandler : function(handler, scope){
31230         this.arrowHandler = handler;
31231         this.scope = scope;  
31232     },
31233     
31234     /**
31235      * Focus the button
31236      */
31237     focus : function(){
31238         if(this.el){
31239             this.el.child("button:first").focus();
31240         }
31241     },
31242
31243     // private
31244     onClick : function(e){
31245         e.preventDefault();
31246         if(!this.disabled){
31247             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31248                 if(this.menu && !this.menu.isVisible()){
31249                     this.menu.show(this.el, this.menuAlign);
31250                 }
31251                 this.fireEvent("arrowclick", this, e);
31252                 if(this.arrowHandler){
31253                     this.arrowHandler.call(this.scope || this, this, e);
31254                 }
31255             }else{
31256                 this.fireEvent("click", this, e);
31257                 if(this.handler){
31258                     this.handler.call(this.scope || this, this, e);
31259                 }
31260             }
31261         }
31262     },
31263     // private
31264     onMouseDown : function(e){
31265         if(!this.disabled){
31266             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31267         }
31268     },
31269     // private
31270     onMouseUp : function(e){
31271         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31272     }   
31273 });
31274
31275
31276 // backwards compat
31277 Roo.MenuButton = Roo.SplitButton;/*
31278  * Based on:
31279  * Ext JS Library 1.1.1
31280  * Copyright(c) 2006-2007, Ext JS, LLC.
31281  *
31282  * Originally Released Under LGPL - original licence link has changed is not relivant.
31283  *
31284  * Fork - LGPL
31285  * <script type="text/javascript">
31286  */
31287
31288 /**
31289  * @class Roo.Toolbar
31290  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31291  * Basic Toolbar class.
31292  * @constructor
31293  * Creates a new Toolbar
31294  * @param {Object} container The config object
31295  */ 
31296 Roo.Toolbar = function(container, buttons, config)
31297 {
31298     /// old consturctor format still supported..
31299     if(container instanceof Array){ // omit the container for later rendering
31300         buttons = container;
31301         config = buttons;
31302         container = null;
31303     }
31304     if (typeof(container) == 'object' && container.xtype) {
31305         config = container;
31306         container = config.container;
31307         buttons = config.buttons || []; // not really - use items!!
31308     }
31309     var xitems = [];
31310     if (config && config.items) {
31311         xitems = config.items;
31312         delete config.items;
31313     }
31314     Roo.apply(this, config);
31315     this.buttons = buttons;
31316     
31317     if(container){
31318         this.render(container);
31319     }
31320     this.xitems = xitems;
31321     Roo.each(xitems, function(b) {
31322         this.add(b);
31323     }, this);
31324     
31325 };
31326
31327 Roo.Toolbar.prototype = {
31328     /**
31329      * @cfg {Array} items
31330      * array of button configs or elements to add (will be converted to a MixedCollection)
31331      */
31332     items: false,
31333     /**
31334      * @cfg {String/HTMLElement/Element} container
31335      * The id or element that will contain the toolbar
31336      */
31337     // private
31338     render : function(ct){
31339         this.el = Roo.get(ct);
31340         if(this.cls){
31341             this.el.addClass(this.cls);
31342         }
31343         // using a table allows for vertical alignment
31344         // 100% width is needed by Safari...
31345         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31346         this.tr = this.el.child("tr", true);
31347         var autoId = 0;
31348         this.items = new Roo.util.MixedCollection(false, function(o){
31349             return o.id || ("item" + (++autoId));
31350         });
31351         if(this.buttons){
31352             this.add.apply(this, this.buttons);
31353             delete this.buttons;
31354         }
31355     },
31356
31357     /**
31358      * Adds element(s) to the toolbar -- this function takes a variable number of 
31359      * arguments of mixed type and adds them to the toolbar.
31360      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31361      * <ul>
31362      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31363      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31364      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31365      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31366      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31367      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31368      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31369      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31370      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31371      * </ul>
31372      * @param {Mixed} arg2
31373      * @param {Mixed} etc.
31374      */
31375     add : function(){
31376         var a = arguments, l = a.length;
31377         for(var i = 0; i < l; i++){
31378             this._add(a[i]);
31379         }
31380     },
31381     // private..
31382     _add : function(el) {
31383         
31384         if (el.xtype) {
31385             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31386         }
31387         
31388         if (el.applyTo){ // some kind of form field
31389             return this.addField(el);
31390         } 
31391         if (el.render){ // some kind of Toolbar.Item
31392             return this.addItem(el);
31393         }
31394         if (typeof el == "string"){ // string
31395             if(el == "separator" || el == "-"){
31396                 return this.addSeparator();
31397             }
31398             if (el == " "){
31399                 return this.addSpacer();
31400             }
31401             if(el == "->"){
31402                 return this.addFill();
31403             }
31404             return this.addText(el);
31405             
31406         }
31407         if(el.tagName){ // element
31408             return this.addElement(el);
31409         }
31410         if(typeof el == "object"){ // must be button config?
31411             return this.addButton(el);
31412         }
31413         // and now what?!?!
31414         return false;
31415         
31416     },
31417     
31418     /**
31419      * Add an Xtype element
31420      * @param {Object} xtype Xtype Object
31421      * @return {Object} created Object
31422      */
31423     addxtype : function(e){
31424         return this.add(e);  
31425     },
31426     
31427     /**
31428      * Returns the Element for this toolbar.
31429      * @return {Roo.Element}
31430      */
31431     getEl : function(){
31432         return this.el;  
31433     },
31434     
31435     /**
31436      * Adds a separator
31437      * @return {Roo.Toolbar.Item} The separator item
31438      */
31439     addSeparator : function(){
31440         return this.addItem(new Roo.Toolbar.Separator());
31441     },
31442
31443     /**
31444      * Adds a spacer element
31445      * @return {Roo.Toolbar.Spacer} The spacer item
31446      */
31447     addSpacer : function(){
31448         return this.addItem(new Roo.Toolbar.Spacer());
31449     },
31450
31451     /**
31452      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31453      * @return {Roo.Toolbar.Fill} The fill item
31454      */
31455     addFill : function(){
31456         return this.addItem(new Roo.Toolbar.Fill());
31457     },
31458
31459     /**
31460      * Adds any standard HTML element to the toolbar
31461      * @param {String/HTMLElement/Element} el The element or id of the element to add
31462      * @return {Roo.Toolbar.Item} The element's item
31463      */
31464     addElement : function(el){
31465         return this.addItem(new Roo.Toolbar.Item(el));
31466     },
31467     /**
31468      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31469      * @type Roo.util.MixedCollection  
31470      */
31471     items : false,
31472      
31473     /**
31474      * Adds any Toolbar.Item or subclass
31475      * @param {Roo.Toolbar.Item} item
31476      * @return {Roo.Toolbar.Item} The item
31477      */
31478     addItem : function(item){
31479         var td = this.nextBlock();
31480         item.render(td);
31481         this.items.add(item);
31482         return item;
31483     },
31484     
31485     /**
31486      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31487      * @param {Object/Array} config A button config or array of configs
31488      * @return {Roo.Toolbar.Button/Array}
31489      */
31490     addButton : function(config){
31491         if(config instanceof Array){
31492             var buttons = [];
31493             for(var i = 0, len = config.length; i < len; i++) {
31494                 buttons.push(this.addButton(config[i]));
31495             }
31496             return buttons;
31497         }
31498         var b = config;
31499         if(!(config instanceof Roo.Toolbar.Button)){
31500             b = config.split ?
31501                 new Roo.Toolbar.SplitButton(config) :
31502                 new Roo.Toolbar.Button(config);
31503         }
31504         var td = this.nextBlock();
31505         b.render(td);
31506         this.items.add(b);
31507         return b;
31508     },
31509     
31510     /**
31511      * Adds text to the toolbar
31512      * @param {String} text The text to add
31513      * @return {Roo.Toolbar.Item} The element's item
31514      */
31515     addText : function(text){
31516         return this.addItem(new Roo.Toolbar.TextItem(text));
31517     },
31518     
31519     /**
31520      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31521      * @param {Number} index The index where the item is to be inserted
31522      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31523      * @return {Roo.Toolbar.Button/Item}
31524      */
31525     insertButton : function(index, item){
31526         if(item instanceof Array){
31527             var buttons = [];
31528             for(var i = 0, len = item.length; i < len; i++) {
31529                buttons.push(this.insertButton(index + i, item[i]));
31530             }
31531             return buttons;
31532         }
31533         if (!(item instanceof Roo.Toolbar.Button)){
31534            item = new Roo.Toolbar.Button(item);
31535         }
31536         var td = document.createElement("td");
31537         this.tr.insertBefore(td, this.tr.childNodes[index]);
31538         item.render(td);
31539         this.items.insert(index, item);
31540         return item;
31541     },
31542     
31543     /**
31544      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31545      * @param {Object} config
31546      * @return {Roo.Toolbar.Item} The element's item
31547      */
31548     addDom : function(config, returnEl){
31549         var td = this.nextBlock();
31550         Roo.DomHelper.overwrite(td, config);
31551         var ti = new Roo.Toolbar.Item(td.firstChild);
31552         ti.render(td);
31553         this.items.add(ti);
31554         return ti;
31555     },
31556
31557     /**
31558      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31559      * @type Roo.util.MixedCollection  
31560      */
31561     fields : false,
31562     
31563     /**
31564      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31565      * Note: the field should not have been rendered yet. For a field that has already been
31566      * rendered, use {@link #addElement}.
31567      * @param {Roo.form.Field} field
31568      * @return {Roo.ToolbarItem}
31569      */
31570      
31571       
31572     addField : function(field) {
31573         if (!this.fields) {
31574             var autoId = 0;
31575             this.fields = new Roo.util.MixedCollection(false, function(o){
31576                 return o.id || ("item" + (++autoId));
31577             });
31578
31579         }
31580         
31581         var td = this.nextBlock();
31582         field.render(td);
31583         var ti = new Roo.Toolbar.Item(td.firstChild);
31584         ti.render(td);
31585         this.items.add(ti);
31586         this.fields.add(field);
31587         return ti;
31588     },
31589     /**
31590      * Hide the toolbar
31591      * @method hide
31592      */
31593      
31594       
31595     hide : function()
31596     {
31597         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31598         this.el.child('div').hide();
31599     },
31600     /**
31601      * Show the toolbar
31602      * @method show
31603      */
31604     show : function()
31605     {
31606         this.el.child('div').show();
31607     },
31608       
31609     // private
31610     nextBlock : function(){
31611         var td = document.createElement("td");
31612         this.tr.appendChild(td);
31613         return td;
31614     },
31615
31616     // private
31617     destroy : function(){
31618         if(this.items){ // rendered?
31619             Roo.destroy.apply(Roo, this.items.items);
31620         }
31621         if(this.fields){ // rendered?
31622             Roo.destroy.apply(Roo, this.fields.items);
31623         }
31624         Roo.Element.uncache(this.el, this.tr);
31625     }
31626 };
31627
31628 /**
31629  * @class Roo.Toolbar.Item
31630  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31631  * @constructor
31632  * Creates a new Item
31633  * @param {HTMLElement} el 
31634  */
31635 Roo.Toolbar.Item = function(el){
31636     var cfg = {};
31637     if (typeof (el.xtype) != 'undefined') {
31638         cfg = el;
31639         el = cfg.el;
31640     }
31641     
31642     this.el = Roo.getDom(el);
31643     this.id = Roo.id(this.el);
31644     this.hidden = false;
31645     
31646     this.addEvents({
31647          /**
31648              * @event render
31649              * Fires when the button is rendered
31650              * @param {Button} this
31651              */
31652         'render': true
31653     });
31654     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31655 };
31656 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31657 //Roo.Toolbar.Item.prototype = {
31658     
31659     /**
31660      * Get this item's HTML Element
31661      * @return {HTMLElement}
31662      */
31663     getEl : function(){
31664        return this.el;  
31665     },
31666
31667     // private
31668     render : function(td){
31669         
31670          this.td = td;
31671         td.appendChild(this.el);
31672         
31673         this.fireEvent('render', this);
31674     },
31675     
31676     /**
31677      * Removes and destroys this item.
31678      */
31679     destroy : function(){
31680         this.td.parentNode.removeChild(this.td);
31681     },
31682     
31683     /**
31684      * Shows this item.
31685      */
31686     show: function(){
31687         this.hidden = false;
31688         this.td.style.display = "";
31689     },
31690     
31691     /**
31692      * Hides this item.
31693      */
31694     hide: function(){
31695         this.hidden = true;
31696         this.td.style.display = "none";
31697     },
31698     
31699     /**
31700      * Convenience function for boolean show/hide.
31701      * @param {Boolean} visible true to show/false to hide
31702      */
31703     setVisible: function(visible){
31704         if(visible) {
31705             this.show();
31706         }else{
31707             this.hide();
31708         }
31709     },
31710     
31711     /**
31712      * Try to focus this item.
31713      */
31714     focus : function(){
31715         Roo.fly(this.el).focus();
31716     },
31717     
31718     /**
31719      * Disables this item.
31720      */
31721     disable : function(){
31722         Roo.fly(this.td).addClass("x-item-disabled");
31723         this.disabled = true;
31724         this.el.disabled = true;
31725     },
31726     
31727     /**
31728      * Enables this item.
31729      */
31730     enable : function(){
31731         Roo.fly(this.td).removeClass("x-item-disabled");
31732         this.disabled = false;
31733         this.el.disabled = false;
31734     }
31735 });
31736
31737
31738 /**
31739  * @class Roo.Toolbar.Separator
31740  * @extends Roo.Toolbar.Item
31741  * A simple toolbar separator class
31742  * @constructor
31743  * Creates a new Separator
31744  */
31745 Roo.Toolbar.Separator = function(cfg){
31746     
31747     var s = document.createElement("span");
31748     s.className = "ytb-sep";
31749     if (cfg) {
31750         cfg.el = s;
31751     }
31752     
31753     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31754 };
31755 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31756     enable:Roo.emptyFn,
31757     disable:Roo.emptyFn,
31758     focus:Roo.emptyFn
31759 });
31760
31761 /**
31762  * @class Roo.Toolbar.Spacer
31763  * @extends Roo.Toolbar.Item
31764  * A simple element that adds extra horizontal space to a toolbar.
31765  * @constructor
31766  * Creates a new Spacer
31767  */
31768 Roo.Toolbar.Spacer = function(cfg){
31769     var s = document.createElement("div");
31770     s.className = "ytb-spacer";
31771     if (cfg) {
31772         cfg.el = s;
31773     }
31774     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31775 };
31776 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31777     enable:Roo.emptyFn,
31778     disable:Roo.emptyFn,
31779     focus:Roo.emptyFn
31780 });
31781
31782 /**
31783  * @class Roo.Toolbar.Fill
31784  * @extends Roo.Toolbar.Spacer
31785  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31786  * @constructor
31787  * Creates a new Spacer
31788  */
31789 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31790     // private
31791     render : function(td){
31792         td.style.width = '100%';
31793         Roo.Toolbar.Fill.superclass.render.call(this, td);
31794     }
31795 });
31796
31797 /**
31798  * @class Roo.Toolbar.TextItem
31799  * @extends Roo.Toolbar.Item
31800  * A simple class that renders text directly into a toolbar.
31801  * @constructor
31802  * Creates a new TextItem
31803  * @cfg {string} text 
31804  */
31805 Roo.Toolbar.TextItem = function(cfg){
31806     var  text = cfg || "";
31807     if (typeof(cfg) == 'object') {
31808         text = cfg.text || "";
31809     }  else {
31810         cfg = null;
31811     }
31812     var s = document.createElement("span");
31813     s.className = "ytb-text";
31814     s.innerHTML = text;
31815     if (cfg) {
31816         cfg.el  = s;
31817     }
31818     
31819     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31820 };
31821 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31822     
31823      
31824     enable:Roo.emptyFn,
31825     disable:Roo.emptyFn,
31826     focus:Roo.emptyFn,
31827      /**
31828      * Shows this button
31829      */
31830     show: function(){
31831         this.hidden = false;
31832         this.el.style.display = "";
31833     },
31834     
31835     /**
31836      * Hides this button
31837      */
31838     hide: function(){
31839         this.hidden = true;
31840         this.el.style.display = "none";
31841     }
31842     
31843 });
31844
31845 /**
31846  * @class Roo.Toolbar.Button
31847  * @extends Roo.Button
31848  * A button that renders into a toolbar.
31849  * @constructor
31850  * Creates a new Button
31851  * @param {Object} config A standard {@link Roo.Button} config object
31852  */
31853 Roo.Toolbar.Button = function(config){
31854     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31855 };
31856 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31857 {
31858     
31859     
31860     render : function(td){
31861         this.td = td;
31862         Roo.Toolbar.Button.superclass.render.call(this, td);
31863     },
31864     
31865     /**
31866      * Removes and destroys this button
31867      */
31868     destroy : function(){
31869         Roo.Toolbar.Button.superclass.destroy.call(this);
31870         this.td.parentNode.removeChild(this.td);
31871     },
31872     
31873     /**
31874      * Shows this button
31875      */
31876     show: function(){
31877         this.hidden = false;
31878         this.td.style.display = "";
31879     },
31880     
31881     /**
31882      * Hides this button
31883      */
31884     hide: function(){
31885         this.hidden = true;
31886         this.td.style.display = "none";
31887     },
31888
31889     /**
31890      * Disables this item
31891      */
31892     disable : function(){
31893         Roo.fly(this.td).addClass("x-item-disabled");
31894         this.disabled = true;
31895     },
31896
31897     /**
31898      * Enables this item
31899      */
31900     enable : function(){
31901         Roo.fly(this.td).removeClass("x-item-disabled");
31902         this.disabled = false;
31903     }
31904 });
31905 // backwards compat
31906 Roo.ToolbarButton = Roo.Toolbar.Button;
31907
31908 /**
31909  * @class Roo.Toolbar.SplitButton
31910  * @extends Roo.SplitButton
31911  * A menu button that renders into a toolbar.
31912  * @constructor
31913  * Creates a new SplitButton
31914  * @param {Object} config A standard {@link Roo.SplitButton} config object
31915  */
31916 Roo.Toolbar.SplitButton = function(config){
31917     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31918 };
31919 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31920     render : function(td){
31921         this.td = td;
31922         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31923     },
31924     
31925     /**
31926      * Removes and destroys this button
31927      */
31928     destroy : function(){
31929         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31930         this.td.parentNode.removeChild(this.td);
31931     },
31932     
31933     /**
31934      * Shows this button
31935      */
31936     show: function(){
31937         this.hidden = false;
31938         this.td.style.display = "";
31939     },
31940     
31941     /**
31942      * Hides this button
31943      */
31944     hide: function(){
31945         this.hidden = true;
31946         this.td.style.display = "none";
31947     }
31948 });
31949
31950 // backwards compat
31951 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31952  * Based on:
31953  * Ext JS Library 1.1.1
31954  * Copyright(c) 2006-2007, Ext JS, LLC.
31955  *
31956  * Originally Released Under LGPL - original licence link has changed is not relivant.
31957  *
31958  * Fork - LGPL
31959  * <script type="text/javascript">
31960  */
31961  
31962 /**
31963  * @class Roo.PagingToolbar
31964  * @extends Roo.Toolbar
31965  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31966  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31967  * @constructor
31968  * Create a new PagingToolbar
31969  * @param {Object} config The config object
31970  */
31971 Roo.PagingToolbar = function(el, ds, config)
31972 {
31973     // old args format still supported... - xtype is prefered..
31974     if (typeof(el) == 'object' && el.xtype) {
31975         // created from xtype...
31976         config = el;
31977         ds = el.dataSource;
31978         el = config.container;
31979     }
31980     var items = [];
31981     if (config.items) {
31982         items = config.items;
31983         config.items = [];
31984     }
31985     
31986     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31987     this.ds = ds;
31988     this.cursor = 0;
31989     this.renderButtons(this.el);
31990     this.bind(ds);
31991     
31992     // supprot items array.
31993    
31994     Roo.each(items, function(e) {
31995         this.add(Roo.factory(e));
31996     },this);
31997     
31998 };
31999
32000 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32001    
32002     /**
32003      * @cfg {String/HTMLElement/Element} container
32004      * container The id or element that will contain the toolbar
32005      */
32006     /**
32007      * @cfg {Boolean} displayInfo
32008      * True to display the displayMsg (defaults to false)
32009      */
32010     
32011     
32012     /**
32013      * @cfg {Number} pageSize
32014      * The number of records to display per page (defaults to 20)
32015      */
32016     pageSize: 20,
32017     /**
32018      * @cfg {String} displayMsg
32019      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32020      */
32021     displayMsg : 'Displaying {0} - {1} of {2}',
32022     /**
32023      * @cfg {String} emptyMsg
32024      * The message to display when no records are found (defaults to "No data to display")
32025      */
32026     emptyMsg : 'No data to display',
32027     /**
32028      * Customizable piece of the default paging text (defaults to "Page")
32029      * @type String
32030      */
32031     beforePageText : "Page",
32032     /**
32033      * Customizable piece of the default paging text (defaults to "of %0")
32034      * @type String
32035      */
32036     afterPageText : "of {0}",
32037     /**
32038      * Customizable piece of the default paging text (defaults to "First Page")
32039      * @type String
32040      */
32041     firstText : "First Page",
32042     /**
32043      * Customizable piece of the default paging text (defaults to "Previous Page")
32044      * @type String
32045      */
32046     prevText : "Previous Page",
32047     /**
32048      * Customizable piece of the default paging text (defaults to "Next Page")
32049      * @type String
32050      */
32051     nextText : "Next Page",
32052     /**
32053      * Customizable piece of the default paging text (defaults to "Last Page")
32054      * @type String
32055      */
32056     lastText : "Last Page",
32057     /**
32058      * Customizable piece of the default paging text (defaults to "Refresh")
32059      * @type String
32060      */
32061     refreshText : "Refresh",
32062
32063     // private
32064     renderButtons : function(el){
32065         Roo.PagingToolbar.superclass.render.call(this, el);
32066         this.first = this.addButton({
32067             tooltip: this.firstText,
32068             cls: "x-btn-icon x-grid-page-first",
32069             disabled: true,
32070             handler: this.onClick.createDelegate(this, ["first"])
32071         });
32072         this.prev = this.addButton({
32073             tooltip: this.prevText,
32074             cls: "x-btn-icon x-grid-page-prev",
32075             disabled: true,
32076             handler: this.onClick.createDelegate(this, ["prev"])
32077         });
32078         //this.addSeparator();
32079         this.add(this.beforePageText);
32080         this.field = Roo.get(this.addDom({
32081            tag: "input",
32082            type: "text",
32083            size: "3",
32084            value: "1",
32085            cls: "x-grid-page-number"
32086         }).el);
32087         this.field.on("keydown", this.onPagingKeydown, this);
32088         this.field.on("focus", function(){this.dom.select();});
32089         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32090         this.field.setHeight(18);
32091         //this.addSeparator();
32092         this.next = this.addButton({
32093             tooltip: this.nextText,
32094             cls: "x-btn-icon x-grid-page-next",
32095             disabled: true,
32096             handler: this.onClick.createDelegate(this, ["next"])
32097         });
32098         this.last = this.addButton({
32099             tooltip: this.lastText,
32100             cls: "x-btn-icon x-grid-page-last",
32101             disabled: true,
32102             handler: this.onClick.createDelegate(this, ["last"])
32103         });
32104         //this.addSeparator();
32105         this.loading = this.addButton({
32106             tooltip: this.refreshText,
32107             cls: "x-btn-icon x-grid-loading",
32108             handler: this.onClick.createDelegate(this, ["refresh"])
32109         });
32110
32111         if(this.displayInfo){
32112             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32113         }
32114     },
32115
32116     // private
32117     updateInfo : function(){
32118         if(this.displayEl){
32119             var count = this.ds.getCount();
32120             var msg = count == 0 ?
32121                 this.emptyMsg :
32122                 String.format(
32123                     this.displayMsg,
32124                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32125                 );
32126             this.displayEl.update(msg);
32127         }
32128     },
32129
32130     // private
32131     onLoad : function(ds, r, o){
32132        this.cursor = o.params ? o.params.start : 0;
32133        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32134
32135        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32136        this.field.dom.value = ap;
32137        this.first.setDisabled(ap == 1);
32138        this.prev.setDisabled(ap == 1);
32139        this.next.setDisabled(ap == ps);
32140        this.last.setDisabled(ap == ps);
32141        this.loading.enable();
32142        this.updateInfo();
32143     },
32144
32145     // private
32146     getPageData : function(){
32147         var total = this.ds.getTotalCount();
32148         return {
32149             total : total,
32150             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32151             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32152         };
32153     },
32154
32155     // private
32156     onLoadError : function(){
32157         this.loading.enable();
32158     },
32159
32160     // private
32161     onPagingKeydown : function(e){
32162         var k = e.getKey();
32163         var d = this.getPageData();
32164         if(k == e.RETURN){
32165             var v = this.field.dom.value, pageNum;
32166             if(!v || isNaN(pageNum = parseInt(v, 10))){
32167                 this.field.dom.value = d.activePage;
32168                 return;
32169             }
32170             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32171             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32172             e.stopEvent();
32173         }
32174         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))
32175         {
32176           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32177           this.field.dom.value = pageNum;
32178           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32179           e.stopEvent();
32180         }
32181         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32182         {
32183           var v = this.field.dom.value, pageNum; 
32184           var increment = (e.shiftKey) ? 10 : 1;
32185           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32186             increment *= -1;
32187           }
32188           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32189             this.field.dom.value = d.activePage;
32190             return;
32191           }
32192           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32193           {
32194             this.field.dom.value = parseInt(v, 10) + increment;
32195             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32196             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32197           }
32198           e.stopEvent();
32199         }
32200     },
32201
32202     // private
32203     beforeLoad : function(){
32204         if(this.loading){
32205             this.loading.disable();
32206         }
32207     },
32208
32209     // private
32210     onClick : function(which){
32211         var ds = this.ds;
32212         switch(which){
32213             case "first":
32214                 ds.load({params:{start: 0, limit: this.pageSize}});
32215             break;
32216             case "prev":
32217                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32218             break;
32219             case "next":
32220                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32221             break;
32222             case "last":
32223                 var total = ds.getTotalCount();
32224                 var extra = total % this.pageSize;
32225                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32226                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32227             break;
32228             case "refresh":
32229                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32230             break;
32231         }
32232     },
32233
32234     /**
32235      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32236      * @param {Roo.data.Store} store The data store to unbind
32237      */
32238     unbind : function(ds){
32239         ds.un("beforeload", this.beforeLoad, this);
32240         ds.un("load", this.onLoad, this);
32241         ds.un("loadexception", this.onLoadError, this);
32242         ds.un("remove", this.updateInfo, this);
32243         ds.un("add", this.updateInfo, this);
32244         this.ds = undefined;
32245     },
32246
32247     /**
32248      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32249      * @param {Roo.data.Store} store The data store to bind
32250      */
32251     bind : function(ds){
32252         ds.on("beforeload", this.beforeLoad, this);
32253         ds.on("load", this.onLoad, this);
32254         ds.on("loadexception", this.onLoadError, this);
32255         ds.on("remove", this.updateInfo, this);
32256         ds.on("add", this.updateInfo, this);
32257         this.ds = ds;
32258     }
32259 });/*
32260  * Based on:
32261  * Ext JS Library 1.1.1
32262  * Copyright(c) 2006-2007, Ext JS, LLC.
32263  *
32264  * Originally Released Under LGPL - original licence link has changed is not relivant.
32265  *
32266  * Fork - LGPL
32267  * <script type="text/javascript">
32268  */
32269
32270 /**
32271  * @class Roo.Resizable
32272  * @extends Roo.util.Observable
32273  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32274  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32275  * 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
32276  * the element will be wrapped for you automatically.</p>
32277  * <p>Here is the list of valid resize handles:</p>
32278  * <pre>
32279 Value   Description
32280 ------  -------------------
32281  'n'     north
32282  's'     south
32283  'e'     east
32284  'w'     west
32285  'nw'    northwest
32286  'sw'    southwest
32287  'se'    southeast
32288  'ne'    northeast
32289  'hd'    horizontal drag
32290  'all'   all
32291 </pre>
32292  * <p>Here's an example showing the creation of a typical Resizable:</p>
32293  * <pre><code>
32294 var resizer = new Roo.Resizable("element-id", {
32295     handles: 'all',
32296     minWidth: 200,
32297     minHeight: 100,
32298     maxWidth: 500,
32299     maxHeight: 400,
32300     pinned: true
32301 });
32302 resizer.on("resize", myHandler);
32303 </code></pre>
32304  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32305  * resizer.east.setDisplayed(false);</p>
32306  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32307  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32308  * resize operation's new size (defaults to [0, 0])
32309  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32310  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32311  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32312  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32313  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32314  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32315  * @cfg {Number} width The width of the element in pixels (defaults to null)
32316  * @cfg {Number} height The height of the element in pixels (defaults to null)
32317  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32318  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32319  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32320  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32321  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32322  * in favor of the handles config option (defaults to false)
32323  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32324  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32325  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32326  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32327  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32328  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32329  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32330  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32331  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32332  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32333  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32334  * @constructor
32335  * Create a new resizable component
32336  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32337  * @param {Object} config configuration options
32338   */
32339 Roo.Resizable = function(el, config)
32340 {
32341     this.el = Roo.get(el);
32342
32343     if(config && config.wrap){
32344         config.resizeChild = this.el;
32345         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32346         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32347         this.el.setStyle("overflow", "hidden");
32348         this.el.setPositioning(config.resizeChild.getPositioning());
32349         config.resizeChild.clearPositioning();
32350         if(!config.width || !config.height){
32351             var csize = config.resizeChild.getSize();
32352             this.el.setSize(csize.width, csize.height);
32353         }
32354         if(config.pinned && !config.adjustments){
32355             config.adjustments = "auto";
32356         }
32357     }
32358
32359     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32360     this.proxy.unselectable();
32361     this.proxy.enableDisplayMode('block');
32362
32363     Roo.apply(this, config);
32364
32365     if(this.pinned){
32366         this.disableTrackOver = true;
32367         this.el.addClass("x-resizable-pinned");
32368     }
32369     // if the element isn't positioned, make it relative
32370     var position = this.el.getStyle("position");
32371     if(position != "absolute" && position != "fixed"){
32372         this.el.setStyle("position", "relative");
32373     }
32374     if(!this.handles){ // no handles passed, must be legacy style
32375         this.handles = 's,e,se';
32376         if(this.multiDirectional){
32377             this.handles += ',n,w';
32378         }
32379     }
32380     if(this.handles == "all"){
32381         this.handles = "n s e w ne nw se sw";
32382     }
32383     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32384     var ps = Roo.Resizable.positions;
32385     for(var i = 0, len = hs.length; i < len; i++){
32386         if(hs[i] && ps[hs[i]]){
32387             var pos = ps[hs[i]];
32388             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32389         }
32390     }
32391     // legacy
32392     this.corner = this.southeast;
32393     
32394     // updateBox = the box can move..
32395     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32396         this.updateBox = true;
32397     }
32398
32399     this.activeHandle = null;
32400
32401     if(this.resizeChild){
32402         if(typeof this.resizeChild == "boolean"){
32403             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32404         }else{
32405             this.resizeChild = Roo.get(this.resizeChild, true);
32406         }
32407     }
32408     
32409     if(this.adjustments == "auto"){
32410         var rc = this.resizeChild;
32411         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32412         if(rc && (hw || hn)){
32413             rc.position("relative");
32414             rc.setLeft(hw ? hw.el.getWidth() : 0);
32415             rc.setTop(hn ? hn.el.getHeight() : 0);
32416         }
32417         this.adjustments = [
32418             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32419             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32420         ];
32421     }
32422
32423     if(this.draggable){
32424         this.dd = this.dynamic ?
32425             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32426         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32427     }
32428
32429     // public events
32430     this.addEvents({
32431         /**
32432          * @event beforeresize
32433          * Fired before resize is allowed. Set enabled to false to cancel resize.
32434          * @param {Roo.Resizable} this
32435          * @param {Roo.EventObject} e The mousedown event
32436          */
32437         "beforeresize" : true,
32438         /**
32439          * @event resizing
32440          * Fired a resizing.
32441          * @param {Roo.Resizable} this
32442          * @param {Number} x The new x position
32443          * @param {Number} y The new y position
32444          * @param {Number} w The new w width
32445          * @param {Number} h The new h hight
32446          * @param {Roo.EventObject} e The mouseup event
32447          */
32448         "resizing" : true,
32449         /**
32450          * @event resize
32451          * Fired after a resize.
32452          * @param {Roo.Resizable} this
32453          * @param {Number} width The new width
32454          * @param {Number} height The new height
32455          * @param {Roo.EventObject} e The mouseup event
32456          */
32457         "resize" : true
32458     });
32459
32460     if(this.width !== null && this.height !== null){
32461         this.resizeTo(this.width, this.height);
32462     }else{
32463         this.updateChildSize();
32464     }
32465     if(Roo.isIE){
32466         this.el.dom.style.zoom = 1;
32467     }
32468     Roo.Resizable.superclass.constructor.call(this);
32469 };
32470
32471 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32472         resizeChild : false,
32473         adjustments : [0, 0],
32474         minWidth : 5,
32475         minHeight : 5,
32476         maxWidth : 10000,
32477         maxHeight : 10000,
32478         enabled : true,
32479         animate : false,
32480         duration : .35,
32481         dynamic : false,
32482         handles : false,
32483         multiDirectional : false,
32484         disableTrackOver : false,
32485         easing : 'easeOutStrong',
32486         widthIncrement : 0,
32487         heightIncrement : 0,
32488         pinned : false,
32489         width : null,
32490         height : null,
32491         preserveRatio : false,
32492         transparent: false,
32493         minX: 0,
32494         minY: 0,
32495         draggable: false,
32496
32497         /**
32498          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32499          */
32500         constrainTo: undefined,
32501         /**
32502          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32503          */
32504         resizeRegion: undefined,
32505
32506
32507     /**
32508      * Perform a manual resize
32509      * @param {Number} width
32510      * @param {Number} height
32511      */
32512     resizeTo : function(width, height){
32513         this.el.setSize(width, height);
32514         this.updateChildSize();
32515         this.fireEvent("resize", this, width, height, null);
32516     },
32517
32518     // private
32519     startSizing : function(e, handle){
32520         this.fireEvent("beforeresize", this, e);
32521         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32522
32523             if(!this.overlay){
32524                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32525                 this.overlay.unselectable();
32526                 this.overlay.enableDisplayMode("block");
32527                 this.overlay.on("mousemove", this.onMouseMove, this);
32528                 this.overlay.on("mouseup", this.onMouseUp, this);
32529             }
32530             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32531
32532             this.resizing = true;
32533             this.startBox = this.el.getBox();
32534             this.startPoint = e.getXY();
32535             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32536                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32537
32538             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32539             this.overlay.show();
32540
32541             if(this.constrainTo) {
32542                 var ct = Roo.get(this.constrainTo);
32543                 this.resizeRegion = ct.getRegion().adjust(
32544                     ct.getFrameWidth('t'),
32545                     ct.getFrameWidth('l'),
32546                     -ct.getFrameWidth('b'),
32547                     -ct.getFrameWidth('r')
32548                 );
32549             }
32550
32551             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32552             this.proxy.show();
32553             this.proxy.setBox(this.startBox);
32554             if(!this.dynamic){
32555                 this.proxy.setStyle('visibility', 'visible');
32556             }
32557         }
32558     },
32559
32560     // private
32561     onMouseDown : function(handle, e){
32562         if(this.enabled){
32563             e.stopEvent();
32564             this.activeHandle = handle;
32565             this.startSizing(e, handle);
32566         }
32567     },
32568
32569     // private
32570     onMouseUp : function(e){
32571         var size = this.resizeElement();
32572         this.resizing = false;
32573         this.handleOut();
32574         this.overlay.hide();
32575         this.proxy.hide();
32576         this.fireEvent("resize", this, size.width, size.height, e);
32577     },
32578
32579     // private
32580     updateChildSize : function(){
32581         
32582         if(this.resizeChild){
32583             var el = this.el;
32584             var child = this.resizeChild;
32585             var adj = this.adjustments;
32586             if(el.dom.offsetWidth){
32587                 var b = el.getSize(true);
32588                 child.setSize(b.width+adj[0], b.height+adj[1]);
32589             }
32590             // Second call here for IE
32591             // The first call enables instant resizing and
32592             // the second call corrects scroll bars if they
32593             // exist
32594             if(Roo.isIE){
32595                 setTimeout(function(){
32596                     if(el.dom.offsetWidth){
32597                         var b = el.getSize(true);
32598                         child.setSize(b.width+adj[0], b.height+adj[1]);
32599                     }
32600                 }, 10);
32601             }
32602         }
32603     },
32604
32605     // private
32606     snap : function(value, inc, min){
32607         if(!inc || !value) {
32608             return value;
32609         }
32610         var newValue = value;
32611         var m = value % inc;
32612         if(m > 0){
32613             if(m > (inc/2)){
32614                 newValue = value + (inc-m);
32615             }else{
32616                 newValue = value - m;
32617             }
32618         }
32619         return Math.max(min, newValue);
32620     },
32621
32622     // private
32623     resizeElement : function(){
32624         var box = this.proxy.getBox();
32625         if(this.updateBox){
32626             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32627         }else{
32628             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32629         }
32630         this.updateChildSize();
32631         if(!this.dynamic){
32632             this.proxy.hide();
32633         }
32634         return box;
32635     },
32636
32637     // private
32638     constrain : function(v, diff, m, mx){
32639         if(v - diff < m){
32640             diff = v - m;
32641         }else if(v - diff > mx){
32642             diff = mx - v;
32643         }
32644         return diff;
32645     },
32646
32647     // private
32648     onMouseMove : function(e){
32649         
32650         if(this.enabled){
32651             try{// try catch so if something goes wrong the user doesn't get hung
32652
32653             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32654                 return;
32655             }
32656
32657             //var curXY = this.startPoint;
32658             var curSize = this.curSize || this.startBox;
32659             var x = this.startBox.x, y = this.startBox.y;
32660             var ox = x, oy = y;
32661             var w = curSize.width, h = curSize.height;
32662             var ow = w, oh = h;
32663             var mw = this.minWidth, mh = this.minHeight;
32664             var mxw = this.maxWidth, mxh = this.maxHeight;
32665             var wi = this.widthIncrement;
32666             var hi = this.heightIncrement;
32667
32668             var eventXY = e.getXY();
32669             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32670             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32671
32672             var pos = this.activeHandle.position;
32673
32674             switch(pos){
32675                 case "east":
32676                     w += diffX;
32677                     w = Math.min(Math.max(mw, w), mxw);
32678                     break;
32679              
32680                 case "south":
32681                     h += diffY;
32682                     h = Math.min(Math.max(mh, h), mxh);
32683                     break;
32684                 case "southeast":
32685                     w += diffX;
32686                     h += diffY;
32687                     w = Math.min(Math.max(mw, w), mxw);
32688                     h = Math.min(Math.max(mh, h), mxh);
32689                     break;
32690                 case "north":
32691                     diffY = this.constrain(h, diffY, mh, mxh);
32692                     y += diffY;
32693                     h -= diffY;
32694                     break;
32695                 case "hdrag":
32696                     
32697                     if (wi) {
32698                         var adiffX = Math.abs(diffX);
32699                         var sub = (adiffX % wi); // how much 
32700                         if (sub > (wi/2)) { // far enough to snap
32701                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32702                         } else {
32703                             // remove difference.. 
32704                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32705                         }
32706                     }
32707                     x += diffX;
32708                     x = Math.max(this.minX, x);
32709                     break;
32710                 case "west":
32711                     diffX = this.constrain(w, diffX, mw, mxw);
32712                     x += diffX;
32713                     w -= diffX;
32714                     break;
32715                 case "northeast":
32716                     w += diffX;
32717                     w = Math.min(Math.max(mw, w), mxw);
32718                     diffY = this.constrain(h, diffY, mh, mxh);
32719                     y += diffY;
32720                     h -= diffY;
32721                     break;
32722                 case "northwest":
32723                     diffX = this.constrain(w, diffX, mw, mxw);
32724                     diffY = this.constrain(h, diffY, mh, mxh);
32725                     y += diffY;
32726                     h -= diffY;
32727                     x += diffX;
32728                     w -= diffX;
32729                     break;
32730                case "southwest":
32731                     diffX = this.constrain(w, diffX, mw, mxw);
32732                     h += diffY;
32733                     h = Math.min(Math.max(mh, h), mxh);
32734                     x += diffX;
32735                     w -= diffX;
32736                     break;
32737             }
32738
32739             var sw = this.snap(w, wi, mw);
32740             var sh = this.snap(h, hi, mh);
32741             if(sw != w || sh != h){
32742                 switch(pos){
32743                     case "northeast":
32744                         y -= sh - h;
32745                     break;
32746                     case "north":
32747                         y -= sh - h;
32748                         break;
32749                     case "southwest":
32750                         x -= sw - w;
32751                     break;
32752                     case "west":
32753                         x -= sw - w;
32754                         break;
32755                     case "northwest":
32756                         x -= sw - w;
32757                         y -= sh - h;
32758                     break;
32759                 }
32760                 w = sw;
32761                 h = sh;
32762             }
32763
32764             if(this.preserveRatio){
32765                 switch(pos){
32766                     case "southeast":
32767                     case "east":
32768                         h = oh * (w/ow);
32769                         h = Math.min(Math.max(mh, h), mxh);
32770                         w = ow * (h/oh);
32771                        break;
32772                     case "south":
32773                         w = ow * (h/oh);
32774                         w = Math.min(Math.max(mw, w), mxw);
32775                         h = oh * (w/ow);
32776                         break;
32777                     case "northeast":
32778                         w = ow * (h/oh);
32779                         w = Math.min(Math.max(mw, w), mxw);
32780                         h = oh * (w/ow);
32781                     break;
32782                     case "north":
32783                         var tw = w;
32784                         w = ow * (h/oh);
32785                         w = Math.min(Math.max(mw, w), mxw);
32786                         h = oh * (w/ow);
32787                         x += (tw - w) / 2;
32788                         break;
32789                     case "southwest":
32790                         h = oh * (w/ow);
32791                         h = Math.min(Math.max(mh, h), mxh);
32792                         var tw = w;
32793                         w = ow * (h/oh);
32794                         x += tw - w;
32795                         break;
32796                     case "west":
32797                         var th = h;
32798                         h = oh * (w/ow);
32799                         h = Math.min(Math.max(mh, h), mxh);
32800                         y += (th - h) / 2;
32801                         var tw = w;
32802                         w = ow * (h/oh);
32803                         x += tw - w;
32804                        break;
32805                     case "northwest":
32806                         var tw = w;
32807                         var th = h;
32808                         h = oh * (w/ow);
32809                         h = Math.min(Math.max(mh, h), mxh);
32810                         w = ow * (h/oh);
32811                         y += th - h;
32812                         x += tw - w;
32813                        break;
32814
32815                 }
32816             }
32817             if (pos == 'hdrag') {
32818                 w = ow;
32819             }
32820             this.proxy.setBounds(x, y, w, h);
32821             if(this.dynamic){
32822                 this.resizeElement();
32823             }
32824             }catch(e){}
32825         }
32826         this.fireEvent("resizing", this, x, y, w, h, e);
32827     },
32828
32829     // private
32830     handleOver : function(){
32831         if(this.enabled){
32832             this.el.addClass("x-resizable-over");
32833         }
32834     },
32835
32836     // private
32837     handleOut : function(){
32838         if(!this.resizing){
32839             this.el.removeClass("x-resizable-over");
32840         }
32841     },
32842
32843     /**
32844      * Returns the element this component is bound to.
32845      * @return {Roo.Element}
32846      */
32847     getEl : function(){
32848         return this.el;
32849     },
32850
32851     /**
32852      * Returns the resizeChild element (or null).
32853      * @return {Roo.Element}
32854      */
32855     getResizeChild : function(){
32856         return this.resizeChild;
32857     },
32858     groupHandler : function()
32859     {
32860         
32861     },
32862     /**
32863      * Destroys this resizable. If the element was wrapped and
32864      * removeEl is not true then the element remains.
32865      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32866      */
32867     destroy : function(removeEl){
32868         this.proxy.remove();
32869         if(this.overlay){
32870             this.overlay.removeAllListeners();
32871             this.overlay.remove();
32872         }
32873         var ps = Roo.Resizable.positions;
32874         for(var k in ps){
32875             if(typeof ps[k] != "function" && this[ps[k]]){
32876                 var h = this[ps[k]];
32877                 h.el.removeAllListeners();
32878                 h.el.remove();
32879             }
32880         }
32881         if(removeEl){
32882             this.el.update("");
32883             this.el.remove();
32884         }
32885     }
32886 });
32887
32888 // private
32889 // hash to map config positions to true positions
32890 Roo.Resizable.positions = {
32891     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32892     hd: "hdrag"
32893 };
32894
32895 // private
32896 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32897     if(!this.tpl){
32898         // only initialize the template if resizable is used
32899         var tpl = Roo.DomHelper.createTemplate(
32900             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32901         );
32902         tpl.compile();
32903         Roo.Resizable.Handle.prototype.tpl = tpl;
32904     }
32905     this.position = pos;
32906     this.rz = rz;
32907     // show north drag fro topdra
32908     var handlepos = pos == 'hdrag' ? 'north' : pos;
32909     
32910     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32911     if (pos == 'hdrag') {
32912         this.el.setStyle('cursor', 'pointer');
32913     }
32914     this.el.unselectable();
32915     if(transparent){
32916         this.el.setOpacity(0);
32917     }
32918     this.el.on("mousedown", this.onMouseDown, this);
32919     if(!disableTrackOver){
32920         this.el.on("mouseover", this.onMouseOver, this);
32921         this.el.on("mouseout", this.onMouseOut, this);
32922     }
32923 };
32924
32925 // private
32926 Roo.Resizable.Handle.prototype = {
32927     afterResize : function(rz){
32928         Roo.log('after?');
32929         // do nothing
32930     },
32931     // private
32932     onMouseDown : function(e){
32933         this.rz.onMouseDown(this, e);
32934     },
32935     // private
32936     onMouseOver : function(e){
32937         this.rz.handleOver(this, e);
32938     },
32939     // private
32940     onMouseOut : function(e){
32941         this.rz.handleOut(this, e);
32942     }
32943 };/*
32944  * Based on:
32945  * Ext JS Library 1.1.1
32946  * Copyright(c) 2006-2007, Ext JS, LLC.
32947  *
32948  * Originally Released Under LGPL - original licence link has changed is not relivant.
32949  *
32950  * Fork - LGPL
32951  * <script type="text/javascript">
32952  */
32953
32954 /**
32955  * @class Roo.Editor
32956  * @extends Roo.Component
32957  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32958  * @constructor
32959  * Create a new Editor
32960  * @param {Roo.form.Field} field The Field object (or descendant)
32961  * @param {Object} config The config object
32962  */
32963 Roo.Editor = function(field, config){
32964     Roo.Editor.superclass.constructor.call(this, config);
32965     this.field = field;
32966     this.addEvents({
32967         /**
32968              * @event beforestartedit
32969              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32970              * false from the handler of this event.
32971              * @param {Editor} this
32972              * @param {Roo.Element} boundEl The underlying element bound to this editor
32973              * @param {Mixed} value The field value being set
32974              */
32975         "beforestartedit" : true,
32976         /**
32977              * @event startedit
32978              * Fires when this editor is displayed
32979              * @param {Roo.Element} boundEl The underlying element bound to this editor
32980              * @param {Mixed} value The starting field value
32981              */
32982         "startedit" : true,
32983         /**
32984              * @event beforecomplete
32985              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32986              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32987              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32988              * event will not fire since no edit actually occurred.
32989              * @param {Editor} this
32990              * @param {Mixed} value The current field value
32991              * @param {Mixed} startValue The original field value
32992              */
32993         "beforecomplete" : true,
32994         /**
32995              * @event complete
32996              * Fires after editing is complete and any changed value has been written to the underlying field.
32997              * @param {Editor} this
32998              * @param {Mixed} value The current field value
32999              * @param {Mixed} startValue The original field value
33000              */
33001         "complete" : true,
33002         /**
33003          * @event specialkey
33004          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33005          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33006          * @param {Roo.form.Field} this
33007          * @param {Roo.EventObject} e The event object
33008          */
33009         "specialkey" : true
33010     });
33011 };
33012
33013 Roo.extend(Roo.Editor, Roo.Component, {
33014     /**
33015      * @cfg {Boolean/String} autosize
33016      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33017      * or "height" to adopt the height only (defaults to false)
33018      */
33019     /**
33020      * @cfg {Boolean} revertInvalid
33021      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33022      * validation fails (defaults to true)
33023      */
33024     /**
33025      * @cfg {Boolean} ignoreNoChange
33026      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33027      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33028      * will never be ignored.
33029      */
33030     /**
33031      * @cfg {Boolean} hideEl
33032      * False to keep the bound element visible while the editor is displayed (defaults to true)
33033      */
33034     /**
33035      * @cfg {Mixed} value
33036      * The data value of the underlying field (defaults to "")
33037      */
33038     value : "",
33039     /**
33040      * @cfg {String} alignment
33041      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33042      */
33043     alignment: "c-c?",
33044     /**
33045      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33046      * for bottom-right shadow (defaults to "frame")
33047      */
33048     shadow : "frame",
33049     /**
33050      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33051      */
33052     constrain : false,
33053     /**
33054      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33055      */
33056     completeOnEnter : false,
33057     /**
33058      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33059      */
33060     cancelOnEsc : false,
33061     /**
33062      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33063      */
33064     updateEl : false,
33065
33066     // private
33067     onRender : function(ct, position){
33068         this.el = new Roo.Layer({
33069             shadow: this.shadow,
33070             cls: "x-editor",
33071             parentEl : ct,
33072             shim : this.shim,
33073             shadowOffset:4,
33074             id: this.id,
33075             constrain: this.constrain
33076         });
33077         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33078         if(this.field.msgTarget != 'title'){
33079             this.field.msgTarget = 'qtip';
33080         }
33081         this.field.render(this.el);
33082         if(Roo.isGecko){
33083             this.field.el.dom.setAttribute('autocomplete', 'off');
33084         }
33085         this.field.on("specialkey", this.onSpecialKey, this);
33086         if(this.swallowKeys){
33087             this.field.el.swallowEvent(['keydown','keypress']);
33088         }
33089         this.field.show();
33090         this.field.on("blur", this.onBlur, this);
33091         if(this.field.grow){
33092             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33093         }
33094     },
33095
33096     onSpecialKey : function(field, e)
33097     {
33098         //Roo.log('editor onSpecialKey');
33099         if(this.completeOnEnter && e.getKey() == e.ENTER){
33100             e.stopEvent();
33101             this.completeEdit();
33102             return;
33103         }
33104         // do not fire special key otherwise it might hide close the editor...
33105         if(e.getKey() == e.ENTER){    
33106             return;
33107         }
33108         if(this.cancelOnEsc && e.getKey() == e.ESC){
33109             this.cancelEdit();
33110             return;
33111         } 
33112         this.fireEvent('specialkey', field, e);
33113     
33114     },
33115
33116     /**
33117      * Starts the editing process and shows the editor.
33118      * @param {String/HTMLElement/Element} el The element to edit
33119      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33120       * to the innerHTML of el.
33121      */
33122     startEdit : function(el, value){
33123         if(this.editing){
33124             this.completeEdit();
33125         }
33126         this.boundEl = Roo.get(el);
33127         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33128         if(!this.rendered){
33129             this.render(this.parentEl || document.body);
33130         }
33131         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33132             return;
33133         }
33134         this.startValue = v;
33135         this.field.setValue(v);
33136         if(this.autoSize){
33137             var sz = this.boundEl.getSize();
33138             switch(this.autoSize){
33139                 case "width":
33140                 this.setSize(sz.width,  "");
33141                 break;
33142                 case "height":
33143                 this.setSize("",  sz.height);
33144                 break;
33145                 default:
33146                 this.setSize(sz.width,  sz.height);
33147             }
33148         }
33149         this.el.alignTo(this.boundEl, this.alignment);
33150         this.editing = true;
33151         if(Roo.QuickTips){
33152             Roo.QuickTips.disable();
33153         }
33154         this.show();
33155     },
33156
33157     /**
33158      * Sets the height and width of this editor.
33159      * @param {Number} width The new width
33160      * @param {Number} height The new height
33161      */
33162     setSize : function(w, h){
33163         this.field.setSize(w, h);
33164         if(this.el){
33165             this.el.sync();
33166         }
33167     },
33168
33169     /**
33170      * Realigns the editor to the bound field based on the current alignment config value.
33171      */
33172     realign : function(){
33173         this.el.alignTo(this.boundEl, this.alignment);
33174     },
33175
33176     /**
33177      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33178      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33179      */
33180     completeEdit : function(remainVisible){
33181         if(!this.editing){
33182             return;
33183         }
33184         var v = this.getValue();
33185         if(this.revertInvalid !== false && !this.field.isValid()){
33186             v = this.startValue;
33187             this.cancelEdit(true);
33188         }
33189         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33190             this.editing = false;
33191             this.hide();
33192             return;
33193         }
33194         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33195             this.editing = false;
33196             if(this.updateEl && this.boundEl){
33197                 this.boundEl.update(v);
33198             }
33199             if(remainVisible !== true){
33200                 this.hide();
33201             }
33202             this.fireEvent("complete", this, v, this.startValue);
33203         }
33204     },
33205
33206     // private
33207     onShow : function(){
33208         this.el.show();
33209         if(this.hideEl !== false){
33210             this.boundEl.hide();
33211         }
33212         this.field.show();
33213         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33214             this.fixIEFocus = true;
33215             this.deferredFocus.defer(50, this);
33216         }else{
33217             this.field.focus();
33218         }
33219         this.fireEvent("startedit", this.boundEl, this.startValue);
33220     },
33221
33222     deferredFocus : function(){
33223         if(this.editing){
33224             this.field.focus();
33225         }
33226     },
33227
33228     /**
33229      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33230      * reverted to the original starting value.
33231      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33232      * cancel (defaults to false)
33233      */
33234     cancelEdit : function(remainVisible){
33235         if(this.editing){
33236             this.setValue(this.startValue);
33237             if(remainVisible !== true){
33238                 this.hide();
33239             }
33240         }
33241     },
33242
33243     // private
33244     onBlur : function(){
33245         if(this.allowBlur !== true && this.editing){
33246             this.completeEdit();
33247         }
33248     },
33249
33250     // private
33251     onHide : function(){
33252         if(this.editing){
33253             this.completeEdit();
33254             return;
33255         }
33256         this.field.blur();
33257         if(this.field.collapse){
33258             this.field.collapse();
33259         }
33260         this.el.hide();
33261         if(this.hideEl !== false){
33262             this.boundEl.show();
33263         }
33264         if(Roo.QuickTips){
33265             Roo.QuickTips.enable();
33266         }
33267     },
33268
33269     /**
33270      * Sets the data value of the editor
33271      * @param {Mixed} value Any valid value supported by the underlying field
33272      */
33273     setValue : function(v){
33274         this.field.setValue(v);
33275     },
33276
33277     /**
33278      * Gets the data value of the editor
33279      * @return {Mixed} The data value
33280      */
33281     getValue : function(){
33282         return this.field.getValue();
33283     }
33284 });/*
33285  * Based on:
33286  * Ext JS Library 1.1.1
33287  * Copyright(c) 2006-2007, Ext JS, LLC.
33288  *
33289  * Originally Released Under LGPL - original licence link has changed is not relivant.
33290  *
33291  * Fork - LGPL
33292  * <script type="text/javascript">
33293  */
33294  
33295 /**
33296  * @class Roo.BasicDialog
33297  * @extends Roo.util.Observable
33298  * @parent none builder
33299  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33300  * <pre><code>
33301 var dlg = new Roo.BasicDialog("my-dlg", {
33302     height: 200,
33303     width: 300,
33304     minHeight: 100,
33305     minWidth: 150,
33306     modal: true,
33307     proxyDrag: true,
33308     shadow: true
33309 });
33310 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33311 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33312 dlg.addButton('Cancel', dlg.hide, dlg);
33313 dlg.show();
33314 </code></pre>
33315   <b>A Dialog should always be a direct child of the body element.</b>
33316  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33317  * @cfg {String} title Default text to display in the title bar (defaults to null)
33318  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33319  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33320  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33321  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33322  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33323  * (defaults to null with no animation)
33324  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33325  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33326  * property for valid values (defaults to 'all')
33327  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33328  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33329  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33330  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33331  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33332  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33333  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33334  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33335  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33336  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33337  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33338  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33339  * draggable = true (defaults to false)
33340  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33341  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33342  * shadow (defaults to false)
33343  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33344  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33345  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33346  * @cfg {Array} buttons Array of buttons
33347  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33348  * @constructor
33349  * Create a new BasicDialog.
33350  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33351  * @param {Object} config Configuration options
33352  */
33353 Roo.BasicDialog = function(el, config){
33354     this.el = Roo.get(el);
33355     var dh = Roo.DomHelper;
33356     if(!this.el && config && config.autoCreate){
33357         if(typeof config.autoCreate == "object"){
33358             if(!config.autoCreate.id){
33359                 config.autoCreate.id = el;
33360             }
33361             this.el = dh.append(document.body,
33362                         config.autoCreate, true);
33363         }else{
33364             this.el = dh.append(document.body,
33365                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33366         }
33367     }
33368     el = this.el;
33369     el.setDisplayed(true);
33370     el.hide = this.hideAction;
33371     this.id = el.id;
33372     el.addClass("x-dlg");
33373
33374     Roo.apply(this, config);
33375
33376     this.proxy = el.createProxy("x-dlg-proxy");
33377     this.proxy.hide = this.hideAction;
33378     this.proxy.setOpacity(.5);
33379     this.proxy.hide();
33380
33381     if(config.width){
33382         el.setWidth(config.width);
33383     }
33384     if(config.height){
33385         el.setHeight(config.height);
33386     }
33387     this.size = el.getSize();
33388     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33389         this.xy = [config.x,config.y];
33390     }else{
33391         this.xy = el.getCenterXY(true);
33392     }
33393     /** The header element @type Roo.Element */
33394     this.header = el.child("> .x-dlg-hd");
33395     /** The body element @type Roo.Element */
33396     this.body = el.child("> .x-dlg-bd");
33397     /** The footer element @type Roo.Element */
33398     this.footer = el.child("> .x-dlg-ft");
33399
33400     if(!this.header){
33401         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33402     }
33403     if(!this.body){
33404         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33405     }
33406
33407     this.header.unselectable();
33408     if(this.title){
33409         this.header.update(this.title);
33410     }
33411     // this element allows the dialog to be focused for keyboard event
33412     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33413     this.focusEl.swallowEvent("click", true);
33414
33415     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33416
33417     // wrap the body and footer for special rendering
33418     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33419     if(this.footer){
33420         this.bwrap.dom.appendChild(this.footer.dom);
33421     }
33422
33423     this.bg = this.el.createChild({
33424         tag: "div", cls:"x-dlg-bg",
33425         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33426     });
33427     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33428
33429
33430     if(this.autoScroll !== false && !this.autoTabs){
33431         this.body.setStyle("overflow", "auto");
33432     }
33433
33434     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33435
33436     if(this.closable !== false){
33437         this.el.addClass("x-dlg-closable");
33438         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33439         this.close.on("click", this.closeClick, this);
33440         this.close.addClassOnOver("x-dlg-close-over");
33441     }
33442     if(this.collapsible !== false){
33443         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33444         this.collapseBtn.on("click", this.collapseClick, this);
33445         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33446         this.header.on("dblclick", this.collapseClick, this);
33447     }
33448     if(this.resizable !== false){
33449         this.el.addClass("x-dlg-resizable");
33450         this.resizer = new Roo.Resizable(el, {
33451             minWidth: this.minWidth || 80,
33452             minHeight:this.minHeight || 80,
33453             handles: this.resizeHandles || "all",
33454             pinned: true
33455         });
33456         this.resizer.on("beforeresize", this.beforeResize, this);
33457         this.resizer.on("resize", this.onResize, this);
33458     }
33459     if(this.draggable !== false){
33460         el.addClass("x-dlg-draggable");
33461         if (!this.proxyDrag) {
33462             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33463         }
33464         else {
33465             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33466         }
33467         dd.setHandleElId(this.header.id);
33468         dd.endDrag = this.endMove.createDelegate(this);
33469         dd.startDrag = this.startMove.createDelegate(this);
33470         dd.onDrag = this.onDrag.createDelegate(this);
33471         dd.scroll = false;
33472         this.dd = dd;
33473     }
33474     if(this.modal){
33475         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33476         this.mask.enableDisplayMode("block");
33477         this.mask.hide();
33478         this.el.addClass("x-dlg-modal");
33479     }
33480     if(this.shadow){
33481         this.shadow = new Roo.Shadow({
33482             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33483             offset : this.shadowOffset
33484         });
33485     }else{
33486         this.shadowOffset = 0;
33487     }
33488     if(Roo.useShims && this.shim !== false){
33489         this.shim = this.el.createShim();
33490         this.shim.hide = this.hideAction;
33491         this.shim.hide();
33492     }else{
33493         this.shim = false;
33494     }
33495     if(this.autoTabs){
33496         this.initTabs();
33497     }
33498     if (this.buttons) { 
33499         var bts= this.buttons;
33500         this.buttons = [];
33501         Roo.each(bts, function(b) {
33502             this.addButton(b);
33503         }, this);
33504     }
33505     
33506     
33507     this.addEvents({
33508         /**
33509          * @event keydown
33510          * Fires when a key is pressed
33511          * @param {Roo.BasicDialog} this
33512          * @param {Roo.EventObject} e
33513          */
33514         "keydown" : true,
33515         /**
33516          * @event move
33517          * Fires when this dialog is moved by the user.
33518          * @param {Roo.BasicDialog} this
33519          * @param {Number} x The new page X
33520          * @param {Number} y The new page Y
33521          */
33522         "move" : true,
33523         /**
33524          * @event resize
33525          * Fires when this dialog is resized by the user.
33526          * @param {Roo.BasicDialog} this
33527          * @param {Number} width The new width
33528          * @param {Number} height The new height
33529          */
33530         "resize" : true,
33531         /**
33532          * @event beforehide
33533          * Fires before this dialog is hidden.
33534          * @param {Roo.BasicDialog} this
33535          */
33536         "beforehide" : true,
33537         /**
33538          * @event hide
33539          * Fires when this dialog is hidden.
33540          * @param {Roo.BasicDialog} this
33541          */
33542         "hide" : true,
33543         /**
33544          * @event beforeshow
33545          * Fires before this dialog is shown.
33546          * @param {Roo.BasicDialog} this
33547          */
33548         "beforeshow" : true,
33549         /**
33550          * @event show
33551          * Fires when this dialog is shown.
33552          * @param {Roo.BasicDialog} this
33553          */
33554         "show" : true
33555     });
33556     el.on("keydown", this.onKeyDown, this);
33557     el.on("mousedown", this.toFront, this);
33558     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33559     this.el.hide();
33560     Roo.DialogManager.register(this);
33561     Roo.BasicDialog.superclass.constructor.call(this);
33562 };
33563
33564 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33565     shadowOffset: Roo.isIE ? 6 : 5,
33566     minHeight: 80,
33567     minWidth: 200,
33568     minButtonWidth: 75,
33569     defaultButton: null,
33570     buttonAlign: "right",
33571     tabTag: 'div',
33572     firstShow: true,
33573
33574     /**
33575      * Sets the dialog title text
33576      * @param {String} text The title text to display
33577      * @return {Roo.BasicDialog} this
33578      */
33579     setTitle : function(text){
33580         this.header.update(text);
33581         return this;
33582     },
33583
33584     // private
33585     closeClick : function(){
33586         this.hide();
33587     },
33588
33589     // private
33590     collapseClick : function(){
33591         this[this.collapsed ? "expand" : "collapse"]();
33592     },
33593
33594     /**
33595      * Collapses the dialog to its minimized state (only the title bar is visible).
33596      * Equivalent to the user clicking the collapse dialog button.
33597      */
33598     collapse : function(){
33599         if(!this.collapsed){
33600             this.collapsed = true;
33601             this.el.addClass("x-dlg-collapsed");
33602             this.restoreHeight = this.el.getHeight();
33603             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33604         }
33605     },
33606
33607     /**
33608      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33609      * clicking the expand dialog button.
33610      */
33611     expand : function(){
33612         if(this.collapsed){
33613             this.collapsed = false;
33614             this.el.removeClass("x-dlg-collapsed");
33615             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33616         }
33617     },
33618
33619     /**
33620      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33621      * @return {Roo.TabPanel} The tabs component
33622      */
33623     initTabs : function(){
33624         var tabs = this.getTabs();
33625         while(tabs.getTab(0)){
33626             tabs.removeTab(0);
33627         }
33628         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33629             var dom = el.dom;
33630             tabs.addTab(Roo.id(dom), dom.title);
33631             dom.title = "";
33632         });
33633         tabs.activate(0);
33634         return tabs;
33635     },
33636
33637     // private
33638     beforeResize : function(){
33639         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33640     },
33641
33642     // private
33643     onResize : function(){
33644         this.refreshSize();
33645         this.syncBodyHeight();
33646         this.adjustAssets();
33647         this.focus();
33648         this.fireEvent("resize", this, this.size.width, this.size.height);
33649     },
33650
33651     // private
33652     onKeyDown : function(e){
33653         if(this.isVisible()){
33654             this.fireEvent("keydown", this, e);
33655         }
33656     },
33657
33658     /**
33659      * Resizes the dialog.
33660      * @param {Number} width
33661      * @param {Number} height
33662      * @return {Roo.BasicDialog} this
33663      */
33664     resizeTo : function(width, height){
33665         this.el.setSize(width, height);
33666         this.size = {width: width, height: height};
33667         this.syncBodyHeight();
33668         if(this.fixedcenter){
33669             this.center();
33670         }
33671         if(this.isVisible()){
33672             this.constrainXY();
33673             this.adjustAssets();
33674         }
33675         this.fireEvent("resize", this, width, height);
33676         return this;
33677     },
33678
33679
33680     /**
33681      * Resizes the dialog to fit the specified content size.
33682      * @param {Number} width
33683      * @param {Number} height
33684      * @return {Roo.BasicDialog} this
33685      */
33686     setContentSize : function(w, h){
33687         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33688         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33689         //if(!this.el.isBorderBox()){
33690             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33691             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33692         //}
33693         if(this.tabs){
33694             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33695             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33696         }
33697         this.resizeTo(w, h);
33698         return this;
33699     },
33700
33701     /**
33702      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33703      * executed in response to a particular key being pressed while the dialog is active.
33704      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33705      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33706      * @param {Function} fn The function to call
33707      * @param {Object} scope (optional) The scope of the function
33708      * @return {Roo.BasicDialog} this
33709      */
33710     addKeyListener : function(key, fn, scope){
33711         var keyCode, shift, ctrl, alt;
33712         if(typeof key == "object" && !(key instanceof Array)){
33713             keyCode = key["key"];
33714             shift = key["shift"];
33715             ctrl = key["ctrl"];
33716             alt = key["alt"];
33717         }else{
33718             keyCode = key;
33719         }
33720         var handler = function(dlg, e){
33721             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33722                 var k = e.getKey();
33723                 if(keyCode instanceof Array){
33724                     for(var i = 0, len = keyCode.length; i < len; i++){
33725                         if(keyCode[i] == k){
33726                           fn.call(scope || window, dlg, k, e);
33727                           return;
33728                         }
33729                     }
33730                 }else{
33731                     if(k == keyCode){
33732                         fn.call(scope || window, dlg, k, e);
33733                     }
33734                 }
33735             }
33736         };
33737         this.on("keydown", handler);
33738         return this;
33739     },
33740
33741     /**
33742      * Returns the TabPanel component (creates it if it doesn't exist).
33743      * Note: If you wish to simply check for the existence of tabs without creating them,
33744      * check for a null 'tabs' property.
33745      * @return {Roo.TabPanel} The tabs component
33746      */
33747     getTabs : function(){
33748         if(!this.tabs){
33749             this.el.addClass("x-dlg-auto-tabs");
33750             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33751             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33752         }
33753         return this.tabs;
33754     },
33755
33756     /**
33757      * Adds a button to the footer section of the dialog.
33758      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33759      * object or a valid Roo.DomHelper element config
33760      * @param {Function} handler The function called when the button is clicked
33761      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33762      * @return {Roo.Button} The new button
33763      */
33764     addButton : function(config, handler, scope){
33765         var dh = Roo.DomHelper;
33766         if(!this.footer){
33767             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33768         }
33769         if(!this.btnContainer){
33770             var tb = this.footer.createChild({
33771
33772                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33773                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33774             }, null, true);
33775             this.btnContainer = tb.firstChild.firstChild.firstChild;
33776         }
33777         var bconfig = {
33778             handler: handler,
33779             scope: scope,
33780             minWidth: this.minButtonWidth,
33781             hideParent:true
33782         };
33783         if(typeof config == "string"){
33784             bconfig.text = config;
33785         }else{
33786             if(config.tag){
33787                 bconfig.dhconfig = config;
33788             }else{
33789                 Roo.apply(bconfig, config);
33790             }
33791         }
33792         var fc = false;
33793         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33794             bconfig.position = Math.max(0, bconfig.position);
33795             fc = this.btnContainer.childNodes[bconfig.position];
33796         }
33797          
33798         var btn = new Roo.Button(
33799             fc ? 
33800                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33801                 : this.btnContainer.appendChild(document.createElement("td")),
33802             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33803             bconfig
33804         );
33805         this.syncBodyHeight();
33806         if(!this.buttons){
33807             /**
33808              * Array of all the buttons that have been added to this dialog via addButton
33809              * @type Array
33810              */
33811             this.buttons = [];
33812         }
33813         this.buttons.push(btn);
33814         return btn;
33815     },
33816
33817     /**
33818      * Sets the default button to be focused when the dialog is displayed.
33819      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33820      * @return {Roo.BasicDialog} this
33821      */
33822     setDefaultButton : function(btn){
33823         this.defaultButton = btn;
33824         return this;
33825     },
33826
33827     // private
33828     getHeaderFooterHeight : function(safe){
33829         var height = 0;
33830         if(this.header){
33831            height += this.header.getHeight();
33832         }
33833         if(this.footer){
33834            var fm = this.footer.getMargins();
33835             height += (this.footer.getHeight()+fm.top+fm.bottom);
33836         }
33837         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33838         height += this.centerBg.getPadding("tb");
33839         return height;
33840     },
33841
33842     // private
33843     syncBodyHeight : function()
33844     {
33845         var bd = this.body, // the text
33846             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33847             bw = this.bwrap;
33848         var height = this.size.height - this.getHeaderFooterHeight(false);
33849         bd.setHeight(height-bd.getMargins("tb"));
33850         var hh = this.header.getHeight();
33851         var h = this.size.height-hh;
33852         cb.setHeight(h);
33853         
33854         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33855         bw.setHeight(h-cb.getPadding("tb"));
33856         
33857         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33858         bd.setWidth(bw.getWidth(true));
33859         if(this.tabs){
33860             this.tabs.syncHeight();
33861             if(Roo.isIE){
33862                 this.tabs.el.repaint();
33863             }
33864         }
33865     },
33866
33867     /**
33868      * Restores the previous state of the dialog if Roo.state is configured.
33869      * @return {Roo.BasicDialog} this
33870      */
33871     restoreState : function(){
33872         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33873         if(box && box.width){
33874             this.xy = [box.x, box.y];
33875             this.resizeTo(box.width, box.height);
33876         }
33877         return this;
33878     },
33879
33880     // private
33881     beforeShow : function(){
33882         this.expand();
33883         if(this.fixedcenter){
33884             this.xy = this.el.getCenterXY(true);
33885         }
33886         if(this.modal){
33887             Roo.get(document.body).addClass("x-body-masked");
33888             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33889             this.mask.show();
33890         }
33891         this.constrainXY();
33892     },
33893
33894     // private
33895     animShow : function(){
33896         var b = Roo.get(this.animateTarget).getBox();
33897         this.proxy.setSize(b.width, b.height);
33898         this.proxy.setLocation(b.x, b.y);
33899         this.proxy.show();
33900         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33901                     true, .35, this.showEl.createDelegate(this));
33902     },
33903
33904     /**
33905      * Shows the dialog.
33906      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33907      * @return {Roo.BasicDialog} this
33908      */
33909     show : function(animateTarget){
33910         if (this.fireEvent("beforeshow", this) === false){
33911             return;
33912         }
33913         if(this.syncHeightBeforeShow){
33914             this.syncBodyHeight();
33915         }else if(this.firstShow){
33916             this.firstShow = false;
33917             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33918         }
33919         this.animateTarget = animateTarget || this.animateTarget;
33920         if(!this.el.isVisible()){
33921             this.beforeShow();
33922             if(this.animateTarget && Roo.get(this.animateTarget)){
33923                 this.animShow();
33924             }else{
33925                 this.showEl();
33926             }
33927         }
33928         return this;
33929     },
33930
33931     // private
33932     showEl : function(){
33933         this.proxy.hide();
33934         this.el.setXY(this.xy);
33935         this.el.show();
33936         this.adjustAssets(true);
33937         this.toFront();
33938         this.focus();
33939         // IE peekaboo bug - fix found by Dave Fenwick
33940         if(Roo.isIE){
33941             this.el.repaint();
33942         }
33943         this.fireEvent("show", this);
33944     },
33945
33946     /**
33947      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33948      * dialog itself will receive focus.
33949      */
33950     focus : function(){
33951         if(this.defaultButton){
33952             this.defaultButton.focus();
33953         }else{
33954             this.focusEl.focus();
33955         }
33956     },
33957
33958     // private
33959     constrainXY : function(){
33960         if(this.constraintoviewport !== false){
33961             if(!this.viewSize){
33962                 if(this.container){
33963                     var s = this.container.getSize();
33964                     this.viewSize = [s.width, s.height];
33965                 }else{
33966                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33967                 }
33968             }
33969             var s = Roo.get(this.container||document).getScroll();
33970
33971             var x = this.xy[0], y = this.xy[1];
33972             var w = this.size.width, h = this.size.height;
33973             var vw = this.viewSize[0], vh = this.viewSize[1];
33974             // only move it if it needs it
33975             var moved = false;
33976             // first validate right/bottom
33977             if(x + w > vw+s.left){
33978                 x = vw - w;
33979                 moved = true;
33980             }
33981             if(y + h > vh+s.top){
33982                 y = vh - h;
33983                 moved = true;
33984             }
33985             // then make sure top/left isn't negative
33986             if(x < s.left){
33987                 x = s.left;
33988                 moved = true;
33989             }
33990             if(y < s.top){
33991                 y = s.top;
33992                 moved = true;
33993             }
33994             if(moved){
33995                 // cache xy
33996                 this.xy = [x, y];
33997                 if(this.isVisible()){
33998                     this.el.setLocation(x, y);
33999                     this.adjustAssets();
34000                 }
34001             }
34002         }
34003     },
34004
34005     // private
34006     onDrag : function(){
34007         if(!this.proxyDrag){
34008             this.xy = this.el.getXY();
34009             this.adjustAssets();
34010         }
34011     },
34012
34013     // private
34014     adjustAssets : function(doShow){
34015         var x = this.xy[0], y = this.xy[1];
34016         var w = this.size.width, h = this.size.height;
34017         if(doShow === true){
34018             if(this.shadow){
34019                 this.shadow.show(this.el);
34020             }
34021             if(this.shim){
34022                 this.shim.show();
34023             }
34024         }
34025         if(this.shadow && this.shadow.isVisible()){
34026             this.shadow.show(this.el);
34027         }
34028         if(this.shim && this.shim.isVisible()){
34029             this.shim.setBounds(x, y, w, h);
34030         }
34031     },
34032
34033     // private
34034     adjustViewport : function(w, h){
34035         if(!w || !h){
34036             w = Roo.lib.Dom.getViewWidth();
34037             h = Roo.lib.Dom.getViewHeight();
34038         }
34039         // cache the size
34040         this.viewSize = [w, h];
34041         if(this.modal && this.mask.isVisible()){
34042             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34043             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34044         }
34045         if(this.isVisible()){
34046             this.constrainXY();
34047         }
34048     },
34049
34050     /**
34051      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34052      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34053      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34054      */
34055     destroy : function(removeEl){
34056         if(this.isVisible()){
34057             this.animateTarget = null;
34058             this.hide();
34059         }
34060         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34061         if(this.tabs){
34062             this.tabs.destroy(removeEl);
34063         }
34064         Roo.destroy(
34065              this.shim,
34066              this.proxy,
34067              this.resizer,
34068              this.close,
34069              this.mask
34070         );
34071         if(this.dd){
34072             this.dd.unreg();
34073         }
34074         if(this.buttons){
34075            for(var i = 0, len = this.buttons.length; i < len; i++){
34076                this.buttons[i].destroy();
34077            }
34078         }
34079         this.el.removeAllListeners();
34080         if(removeEl === true){
34081             this.el.update("");
34082             this.el.remove();
34083         }
34084         Roo.DialogManager.unregister(this);
34085     },
34086
34087     // private
34088     startMove : function(){
34089         if(this.proxyDrag){
34090             this.proxy.show();
34091         }
34092         if(this.constraintoviewport !== false){
34093             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34094         }
34095     },
34096
34097     // private
34098     endMove : function(){
34099         if(!this.proxyDrag){
34100             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34101         }else{
34102             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34103             this.proxy.hide();
34104         }
34105         this.refreshSize();
34106         this.adjustAssets();
34107         this.focus();
34108         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34109     },
34110
34111     /**
34112      * Brings this dialog to the front of any other visible dialogs
34113      * @return {Roo.BasicDialog} this
34114      */
34115     toFront : function(){
34116         Roo.DialogManager.bringToFront(this);
34117         return this;
34118     },
34119
34120     /**
34121      * Sends this dialog to the back (under) of any other visible dialogs
34122      * @return {Roo.BasicDialog} this
34123      */
34124     toBack : function(){
34125         Roo.DialogManager.sendToBack(this);
34126         return this;
34127     },
34128
34129     /**
34130      * Centers this dialog in the viewport
34131      * @return {Roo.BasicDialog} this
34132      */
34133     center : function(){
34134         var xy = this.el.getCenterXY(true);
34135         this.moveTo(xy[0], xy[1]);
34136         return this;
34137     },
34138
34139     /**
34140      * Moves the dialog's top-left corner to the specified point
34141      * @param {Number} x
34142      * @param {Number} y
34143      * @return {Roo.BasicDialog} this
34144      */
34145     moveTo : function(x, y){
34146         this.xy = [x,y];
34147         if(this.isVisible()){
34148             this.el.setXY(this.xy);
34149             this.adjustAssets();
34150         }
34151         return this;
34152     },
34153
34154     /**
34155      * Aligns the dialog to the specified element
34156      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34157      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34158      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34159      * @return {Roo.BasicDialog} this
34160      */
34161     alignTo : function(element, position, offsets){
34162         this.xy = this.el.getAlignToXY(element, position, offsets);
34163         if(this.isVisible()){
34164             this.el.setXY(this.xy);
34165             this.adjustAssets();
34166         }
34167         return this;
34168     },
34169
34170     /**
34171      * Anchors an element to another element and realigns it when the window is resized.
34172      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34173      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34174      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34175      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34176      * is a number, it is used as the buffer delay (defaults to 50ms).
34177      * @return {Roo.BasicDialog} this
34178      */
34179     anchorTo : function(el, alignment, offsets, monitorScroll){
34180         var action = function(){
34181             this.alignTo(el, alignment, offsets);
34182         };
34183         Roo.EventManager.onWindowResize(action, this);
34184         var tm = typeof monitorScroll;
34185         if(tm != 'undefined'){
34186             Roo.EventManager.on(window, 'scroll', action, this,
34187                 {buffer: tm == 'number' ? monitorScroll : 50});
34188         }
34189         action.call(this);
34190         return this;
34191     },
34192
34193     /**
34194      * Returns true if the dialog is visible
34195      * @return {Boolean}
34196      */
34197     isVisible : function(){
34198         return this.el.isVisible();
34199     },
34200
34201     // private
34202     animHide : function(callback){
34203         var b = Roo.get(this.animateTarget).getBox();
34204         this.proxy.show();
34205         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34206         this.el.hide();
34207         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34208                     this.hideEl.createDelegate(this, [callback]));
34209     },
34210
34211     /**
34212      * Hides the dialog.
34213      * @param {Function} callback (optional) Function to call when the dialog is hidden
34214      * @return {Roo.BasicDialog} this
34215      */
34216     hide : function(callback){
34217         if (this.fireEvent("beforehide", this) === false){
34218             return;
34219         }
34220         if(this.shadow){
34221             this.shadow.hide();
34222         }
34223         if(this.shim) {
34224           this.shim.hide();
34225         }
34226         // sometimes animateTarget seems to get set.. causing problems...
34227         // this just double checks..
34228         if(this.animateTarget && Roo.get(this.animateTarget)) {
34229            this.animHide(callback);
34230         }else{
34231             this.el.hide();
34232             this.hideEl(callback);
34233         }
34234         return this;
34235     },
34236
34237     // private
34238     hideEl : function(callback){
34239         this.proxy.hide();
34240         if(this.modal){
34241             this.mask.hide();
34242             Roo.get(document.body).removeClass("x-body-masked");
34243         }
34244         this.fireEvent("hide", this);
34245         if(typeof callback == "function"){
34246             callback();
34247         }
34248     },
34249
34250     // private
34251     hideAction : function(){
34252         this.setLeft("-10000px");
34253         this.setTop("-10000px");
34254         this.setStyle("visibility", "hidden");
34255     },
34256
34257     // private
34258     refreshSize : function(){
34259         this.size = this.el.getSize();
34260         this.xy = this.el.getXY();
34261         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34262     },
34263
34264     // private
34265     // z-index is managed by the DialogManager and may be overwritten at any time
34266     setZIndex : function(index){
34267         if(this.modal){
34268             this.mask.setStyle("z-index", index);
34269         }
34270         if(this.shim){
34271             this.shim.setStyle("z-index", ++index);
34272         }
34273         if(this.shadow){
34274             this.shadow.setZIndex(++index);
34275         }
34276         this.el.setStyle("z-index", ++index);
34277         if(this.proxy){
34278             this.proxy.setStyle("z-index", ++index);
34279         }
34280         if(this.resizer){
34281             this.resizer.proxy.setStyle("z-index", ++index);
34282         }
34283
34284         this.lastZIndex = index;
34285     },
34286
34287     /**
34288      * Returns the element for this dialog
34289      * @return {Roo.Element} The underlying dialog Element
34290      */
34291     getEl : function(){
34292         return this.el;
34293     }
34294 });
34295
34296 /**
34297  * @class Roo.DialogManager
34298  * Provides global access to BasicDialogs that have been created and
34299  * support for z-indexing (layering) multiple open dialogs.
34300  */
34301 Roo.DialogManager = function(){
34302     var list = {};
34303     var accessList = [];
34304     var front = null;
34305
34306     // private
34307     var sortDialogs = function(d1, d2){
34308         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34309     };
34310
34311     // private
34312     var orderDialogs = function(){
34313         accessList.sort(sortDialogs);
34314         var seed = Roo.DialogManager.zseed;
34315         for(var i = 0, len = accessList.length; i < len; i++){
34316             var dlg = accessList[i];
34317             if(dlg){
34318                 dlg.setZIndex(seed + (i*10));
34319             }
34320         }
34321     };
34322
34323     return {
34324         /**
34325          * The starting z-index for BasicDialogs (defaults to 9000)
34326          * @type Number The z-index value
34327          */
34328         zseed : 9000,
34329
34330         // private
34331         register : function(dlg){
34332             list[dlg.id] = dlg;
34333             accessList.push(dlg);
34334         },
34335
34336         // private
34337         unregister : function(dlg){
34338             delete list[dlg.id];
34339             var i=0;
34340             var len=0;
34341             if(!accessList.indexOf){
34342                 for(  i = 0, len = accessList.length; i < len; i++){
34343                     if(accessList[i] == dlg){
34344                         accessList.splice(i, 1);
34345                         return;
34346                     }
34347                 }
34348             }else{
34349                  i = accessList.indexOf(dlg);
34350                 if(i != -1){
34351                     accessList.splice(i, 1);
34352                 }
34353             }
34354         },
34355
34356         /**
34357          * Gets a registered dialog by id
34358          * @param {String/Object} id The id of the dialog or a dialog
34359          * @return {Roo.BasicDialog} this
34360          */
34361         get : function(id){
34362             return typeof id == "object" ? id : list[id];
34363         },
34364
34365         /**
34366          * Brings the specified dialog to the front
34367          * @param {String/Object} dlg The id of the dialog or a dialog
34368          * @return {Roo.BasicDialog} this
34369          */
34370         bringToFront : function(dlg){
34371             dlg = this.get(dlg);
34372             if(dlg != front){
34373                 front = dlg;
34374                 dlg._lastAccess = new Date().getTime();
34375                 orderDialogs();
34376             }
34377             return dlg;
34378         },
34379
34380         /**
34381          * Sends the specified dialog to the back
34382          * @param {String/Object} dlg The id of the dialog or a dialog
34383          * @return {Roo.BasicDialog} this
34384          */
34385         sendToBack : function(dlg){
34386             dlg = this.get(dlg);
34387             dlg._lastAccess = -(new Date().getTime());
34388             orderDialogs();
34389             return dlg;
34390         },
34391
34392         /**
34393          * Hides all dialogs
34394          */
34395         hideAll : function(){
34396             for(var id in list){
34397                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34398                     list[id].hide();
34399                 }
34400             }
34401         }
34402     };
34403 }();
34404
34405 /**
34406  * @class Roo.LayoutDialog
34407  * @extends Roo.BasicDialog
34408  * @children Roo.ContentPanel
34409  * @parent builder none
34410  * Dialog which provides adjustments for working with a layout in a Dialog.
34411  * Add your necessary layout config options to the dialog's config.<br>
34412  * Example usage (including a nested layout):
34413  * <pre><code>
34414 if(!dialog){
34415     dialog = new Roo.LayoutDialog("download-dlg", {
34416         modal: true,
34417         width:600,
34418         height:450,
34419         shadow:true,
34420         minWidth:500,
34421         minHeight:350,
34422         autoTabs:true,
34423         proxyDrag:true,
34424         // layout config merges with the dialog config
34425         center:{
34426             tabPosition: "top",
34427             alwaysShowTabs: true
34428         }
34429     });
34430     dialog.addKeyListener(27, dialog.hide, dialog);
34431     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34432     dialog.addButton("Build It!", this.getDownload, this);
34433
34434     // we can even add nested layouts
34435     var innerLayout = new Roo.BorderLayout("dl-inner", {
34436         east: {
34437             initialSize: 200,
34438             autoScroll:true,
34439             split:true
34440         },
34441         center: {
34442             autoScroll:true
34443         }
34444     });
34445     innerLayout.beginUpdate();
34446     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34447     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34448     innerLayout.endUpdate(true);
34449
34450     var layout = dialog.getLayout();
34451     layout.beginUpdate();
34452     layout.add("center", new Roo.ContentPanel("standard-panel",
34453                         {title: "Download the Source", fitToFrame:true}));
34454     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34455                {title: "Build your own roo.js"}));
34456     layout.getRegion("center").showPanel(sp);
34457     layout.endUpdate();
34458 }
34459 </code></pre>
34460     * @constructor
34461     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34462     * @param {Object} config configuration options
34463   */
34464 Roo.LayoutDialog = function(el, cfg){
34465     
34466     var config=  cfg;
34467     if (typeof(cfg) == 'undefined') {
34468         config = Roo.apply({}, el);
34469         // not sure why we use documentElement here.. - it should always be body.
34470         // IE7 borks horribly if we use documentElement.
34471         // webkit also does not like documentElement - it creates a body element...
34472         el = Roo.get( document.body || document.documentElement ).createChild();
34473         //config.autoCreate = true;
34474     }
34475     
34476     
34477     config.autoTabs = false;
34478     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34479     this.body.setStyle({overflow:"hidden", position:"relative"});
34480     this.layout = new Roo.BorderLayout(this.body.dom, config);
34481     this.layout.monitorWindowResize = false;
34482     this.el.addClass("x-dlg-auto-layout");
34483     // fix case when center region overwrites center function
34484     this.center = Roo.BasicDialog.prototype.center;
34485     this.on("show", this.layout.layout, this.layout, true);
34486     if (config.items) {
34487         var xitems = config.items;
34488         delete config.items;
34489         Roo.each(xitems, this.addxtype, this);
34490     }
34491     
34492     
34493 };
34494 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34495     
34496     
34497     /**
34498      * @cfg {Roo.LayoutRegion} east  
34499      */
34500     /**
34501      * @cfg {Roo.LayoutRegion} west
34502      */
34503     /**
34504      * @cfg {Roo.LayoutRegion} south
34505      */
34506     /**
34507      * @cfg {Roo.LayoutRegion} north
34508      */
34509     /**
34510      * @cfg {Roo.LayoutRegion} center
34511      */
34512     /**
34513      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34514      */
34515     
34516     
34517     /**
34518      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34519      * @deprecated
34520      */
34521     endUpdate : function(){
34522         this.layout.endUpdate();
34523     },
34524
34525     /**
34526      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34527      *  @deprecated
34528      */
34529     beginUpdate : function(){
34530         this.layout.beginUpdate();
34531     },
34532
34533     /**
34534      * Get the BorderLayout for this dialog
34535      * @return {Roo.BorderLayout}
34536      */
34537     getLayout : function(){
34538         return this.layout;
34539     },
34540
34541     showEl : function(){
34542         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34543         if(Roo.isIE7){
34544             this.layout.layout();
34545         }
34546     },
34547
34548     // private
34549     // Use the syncHeightBeforeShow config option to control this automatically
34550     syncBodyHeight : function(){
34551         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34552         if(this.layout){this.layout.layout();}
34553     },
34554     
34555       /**
34556      * Add an xtype element (actually adds to the layout.)
34557      * @return {Object} xdata xtype object data.
34558      */
34559     
34560     addxtype : function(c) {
34561         return this.layout.addxtype(c);
34562     }
34563 });/*
34564  * Based on:
34565  * Ext JS Library 1.1.1
34566  * Copyright(c) 2006-2007, Ext JS, LLC.
34567  *
34568  * Originally Released Under LGPL - original licence link has changed is not relivant.
34569  *
34570  * Fork - LGPL
34571  * <script type="text/javascript">
34572  */
34573  
34574 /**
34575  * @class Roo.MessageBox
34576  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34577  * Example usage:
34578  *<pre><code>
34579 // Basic alert:
34580 Roo.Msg.alert('Status', 'Changes saved successfully.');
34581
34582 // Prompt for user data:
34583 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34584     if (btn == 'ok'){
34585         // process text value...
34586     }
34587 });
34588
34589 // Show a dialog using config options:
34590 Roo.Msg.show({
34591    title:'Save Changes?',
34592    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34593    buttons: Roo.Msg.YESNOCANCEL,
34594    fn: processResult,
34595    animEl: 'elId'
34596 });
34597 </code></pre>
34598  * @static
34599  */
34600 Roo.MessageBox = function(){
34601     var dlg, opt, mask, waitTimer;
34602     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34603     var buttons, activeTextEl, bwidth;
34604
34605     // private
34606     var handleButton = function(button){
34607         dlg.hide();
34608         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34609     };
34610
34611     // private
34612     var handleHide = function(){
34613         if(opt && opt.cls){
34614             dlg.el.removeClass(opt.cls);
34615         }
34616         if(waitTimer){
34617             Roo.TaskMgr.stop(waitTimer);
34618             waitTimer = null;
34619         }
34620     };
34621
34622     // private
34623     var updateButtons = function(b){
34624         var width = 0;
34625         if(!b){
34626             buttons["ok"].hide();
34627             buttons["cancel"].hide();
34628             buttons["yes"].hide();
34629             buttons["no"].hide();
34630             dlg.footer.dom.style.display = 'none';
34631             return width;
34632         }
34633         dlg.footer.dom.style.display = '';
34634         for(var k in buttons){
34635             if(typeof buttons[k] != "function"){
34636                 if(b[k]){
34637                     buttons[k].show();
34638                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34639                     width += buttons[k].el.getWidth()+15;
34640                 }else{
34641                     buttons[k].hide();
34642                 }
34643             }
34644         }
34645         return width;
34646     };
34647
34648     // private
34649     var handleEsc = function(d, k, e){
34650         if(opt && opt.closable !== false){
34651             dlg.hide();
34652         }
34653         if(e){
34654             e.stopEvent();
34655         }
34656     };
34657
34658     return {
34659         /**
34660          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34661          * @return {Roo.BasicDialog} The BasicDialog element
34662          */
34663         getDialog : function(){
34664            if(!dlg){
34665                 dlg = new Roo.BasicDialog("x-msg-box", {
34666                     autoCreate : true,
34667                     shadow: true,
34668                     draggable: true,
34669                     resizable:false,
34670                     constraintoviewport:false,
34671                     fixedcenter:true,
34672                     collapsible : false,
34673                     shim:true,
34674                     modal: true,
34675                     width:400, height:100,
34676                     buttonAlign:"center",
34677                     closeClick : function(){
34678                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34679                             handleButton("no");
34680                         }else{
34681                             handleButton("cancel");
34682                         }
34683                     }
34684                 });
34685                 dlg.on("hide", handleHide);
34686                 mask = dlg.mask;
34687                 dlg.addKeyListener(27, handleEsc);
34688                 buttons = {};
34689                 var bt = this.buttonText;
34690                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34691                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34692                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34693                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34694                 bodyEl = dlg.body.createChild({
34695
34696                     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>'
34697                 });
34698                 msgEl = bodyEl.dom.firstChild;
34699                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34700                 textboxEl.enableDisplayMode();
34701                 textboxEl.addKeyListener([10,13], function(){
34702                     if(dlg.isVisible() && opt && opt.buttons){
34703                         if(opt.buttons.ok){
34704                             handleButton("ok");
34705                         }else if(opt.buttons.yes){
34706                             handleButton("yes");
34707                         }
34708                     }
34709                 });
34710                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34711                 textareaEl.enableDisplayMode();
34712                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34713                 progressEl.enableDisplayMode();
34714                 var pf = progressEl.dom.firstChild;
34715                 if (pf) {
34716                     pp = Roo.get(pf.firstChild);
34717                     pp.setHeight(pf.offsetHeight);
34718                 }
34719                 
34720             }
34721             return dlg;
34722         },
34723
34724         /**
34725          * Updates the message box body text
34726          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34727          * the XHTML-compliant non-breaking space character '&amp;#160;')
34728          * @return {Roo.MessageBox} This message box
34729          */
34730         updateText : function(text){
34731             if(!dlg.isVisible() && !opt.width){
34732                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34733             }
34734             msgEl.innerHTML = text || '&#160;';
34735       
34736             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34737             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34738             var w = Math.max(
34739                     Math.min(opt.width || cw , this.maxWidth), 
34740                     Math.max(opt.minWidth || this.minWidth, bwidth)
34741             );
34742             if(opt.prompt){
34743                 activeTextEl.setWidth(w);
34744             }
34745             if(dlg.isVisible()){
34746                 dlg.fixedcenter = false;
34747             }
34748             // to big, make it scroll. = But as usual stupid IE does not support
34749             // !important..
34750             
34751             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34752                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34753                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34754             } else {
34755                 bodyEl.dom.style.height = '';
34756                 bodyEl.dom.style.overflowY = '';
34757             }
34758             if (cw > w) {
34759                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34760             } else {
34761                 bodyEl.dom.style.overflowX = '';
34762             }
34763             
34764             dlg.setContentSize(w, bodyEl.getHeight());
34765             if(dlg.isVisible()){
34766                 dlg.fixedcenter = true;
34767             }
34768             return this;
34769         },
34770
34771         /**
34772          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34773          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34774          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34775          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34776          * @return {Roo.MessageBox} This message box
34777          */
34778         updateProgress : function(value, text){
34779             if(text){
34780                 this.updateText(text);
34781             }
34782             if (pp) { // weird bug on my firefox - for some reason this is not defined
34783                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34784             }
34785             return this;
34786         },        
34787
34788         /**
34789          * Returns true if the message box is currently displayed
34790          * @return {Boolean} True if the message box is visible, else false
34791          */
34792         isVisible : function(){
34793             return dlg && dlg.isVisible();  
34794         },
34795
34796         /**
34797          * Hides the message box if it is displayed
34798          */
34799         hide : function(){
34800             if(this.isVisible()){
34801                 dlg.hide();
34802             }  
34803         },
34804
34805         /**
34806          * Displays a new message box, or reinitializes an existing message box, based on the config options
34807          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34808          * The following config object properties are supported:
34809          * <pre>
34810 Property    Type             Description
34811 ----------  ---------------  ------------------------------------------------------------------------------------
34812 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34813                                    closes (defaults to undefined)
34814 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34815                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34816 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34817                                    progress and wait dialogs will ignore this property and always hide the
34818                                    close button as they can only be closed programmatically.
34819 cls               String           A custom CSS class to apply to the message box element
34820 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34821                                    displayed (defaults to 75)
34822 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34823                                    function will be btn (the name of the button that was clicked, if applicable,
34824                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34825                                    Progress and wait dialogs will ignore this option since they do not respond to
34826                                    user actions and can only be closed programmatically, so any required function
34827                                    should be called by the same code after it closes the dialog.
34828 icon              String           A CSS class that provides a background image to be used as an icon for
34829                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34830 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34831 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34832 modal             Boolean          False to allow user interaction with the page while the message box is
34833                                    displayed (defaults to true)
34834 msg               String           A string that will replace the existing message box body text (defaults
34835                                    to the XHTML-compliant non-breaking space character '&#160;')
34836 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34837 progress          Boolean          True to display a progress bar (defaults to false)
34838 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34839 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34840 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34841 title             String           The title text
34842 value             String           The string value to set into the active textbox element if displayed
34843 wait              Boolean          True to display a progress bar (defaults to false)
34844 width             Number           The width of the dialog in pixels
34845 </pre>
34846          *
34847          * Example usage:
34848          * <pre><code>
34849 Roo.Msg.show({
34850    title: 'Address',
34851    msg: 'Please enter your address:',
34852    width: 300,
34853    buttons: Roo.MessageBox.OKCANCEL,
34854    multiline: true,
34855    fn: saveAddress,
34856    animEl: 'addAddressBtn'
34857 });
34858 </code></pre>
34859          * @param {Object} config Configuration options
34860          * @return {Roo.MessageBox} This message box
34861          */
34862         show : function(options)
34863         {
34864             
34865             // this causes nightmares if you show one dialog after another
34866             // especially on callbacks..
34867              
34868             if(this.isVisible()){
34869                 
34870                 this.hide();
34871                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34872                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34873                 Roo.log("New Dialog Message:" +  options.msg )
34874                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34875                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34876                 
34877             }
34878             var d = this.getDialog();
34879             opt = options;
34880             d.setTitle(opt.title || "&#160;");
34881             d.close.setDisplayed(opt.closable !== false);
34882             activeTextEl = textboxEl;
34883             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34884             if(opt.prompt){
34885                 if(opt.multiline){
34886                     textboxEl.hide();
34887                     textareaEl.show();
34888                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34889                         opt.multiline : this.defaultTextHeight);
34890                     activeTextEl = textareaEl;
34891                 }else{
34892                     textboxEl.show();
34893                     textareaEl.hide();
34894                 }
34895             }else{
34896                 textboxEl.hide();
34897                 textareaEl.hide();
34898             }
34899             progressEl.setDisplayed(opt.progress === true);
34900             this.updateProgress(0);
34901             activeTextEl.dom.value = opt.value || "";
34902             if(opt.prompt){
34903                 dlg.setDefaultButton(activeTextEl);
34904             }else{
34905                 var bs = opt.buttons;
34906                 var db = null;
34907                 if(bs && bs.ok){
34908                     db = buttons["ok"];
34909                 }else if(bs && bs.yes){
34910                     db = buttons["yes"];
34911                 }
34912                 dlg.setDefaultButton(db);
34913             }
34914             bwidth = updateButtons(opt.buttons);
34915             this.updateText(opt.msg);
34916             if(opt.cls){
34917                 d.el.addClass(opt.cls);
34918             }
34919             d.proxyDrag = opt.proxyDrag === true;
34920             d.modal = opt.modal !== false;
34921             d.mask = opt.modal !== false ? mask : false;
34922             if(!d.isVisible()){
34923                 // force it to the end of the z-index stack so it gets a cursor in FF
34924                 document.body.appendChild(dlg.el.dom);
34925                 d.animateTarget = null;
34926                 d.show(options.animEl);
34927             }
34928             return this;
34929         },
34930
34931         /**
34932          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34933          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34934          * and closing the message box when the process is complete.
34935          * @param {String} title The title bar text
34936          * @param {String} msg The message box body text
34937          * @return {Roo.MessageBox} This message box
34938          */
34939         progress : function(title, msg){
34940             this.show({
34941                 title : title,
34942                 msg : msg,
34943                 buttons: false,
34944                 progress:true,
34945                 closable:false,
34946                 minWidth: this.minProgressWidth,
34947                 modal : true
34948             });
34949             return this;
34950         },
34951
34952         /**
34953          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34954          * If a callback function is passed it will be called after the user clicks the button, and the
34955          * id of the button that was clicked will be passed as the only parameter to the callback
34956          * (could also be the top-right close button).
34957          * @param {String} title The title bar text
34958          * @param {String} msg The message box body text
34959          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34960          * @param {Object} scope (optional) The scope of the callback function
34961          * @return {Roo.MessageBox} This message box
34962          */
34963         alert : function(title, msg, fn, scope){
34964             this.show({
34965                 title : title,
34966                 msg : msg,
34967                 buttons: this.OK,
34968                 fn: fn,
34969                 scope : scope,
34970                 modal : true
34971             });
34972             return this;
34973         },
34974
34975         /**
34976          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34977          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34978          * You are responsible for closing the message box when the process is complete.
34979          * @param {String} msg The message box body text
34980          * @param {String} title (optional) The title bar text
34981          * @return {Roo.MessageBox} This message box
34982          */
34983         wait : function(msg, title){
34984             this.show({
34985                 title : title,
34986                 msg : msg,
34987                 buttons: false,
34988                 closable:false,
34989                 progress:true,
34990                 modal:true,
34991                 width:300,
34992                 wait:true
34993             });
34994             waitTimer = Roo.TaskMgr.start({
34995                 run: function(i){
34996                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34997                 },
34998                 interval: 1000
34999             });
35000             return this;
35001         },
35002
35003         /**
35004          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35005          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35006          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35007          * @param {String} title The title bar text
35008          * @param {String} msg The message box body text
35009          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35010          * @param {Object} scope (optional) The scope of the callback function
35011          * @return {Roo.MessageBox} This message box
35012          */
35013         confirm : function(title, msg, fn, scope){
35014             this.show({
35015                 title : title,
35016                 msg : msg,
35017                 buttons: this.YESNO,
35018                 fn: fn,
35019                 scope : scope,
35020                 modal : true
35021             });
35022             return this;
35023         },
35024
35025         /**
35026          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35027          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35028          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35029          * (could also be the top-right close button) and the text that was entered will be passed as the two
35030          * parameters to the callback.
35031          * @param {String} title The title bar text
35032          * @param {String} msg The message box body text
35033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35034          * @param {Object} scope (optional) The scope of the callback function
35035          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35036          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35037          * @return {Roo.MessageBox} This message box
35038          */
35039         prompt : function(title, msg, fn, scope, multiline){
35040             this.show({
35041                 title : title,
35042                 msg : msg,
35043                 buttons: this.OKCANCEL,
35044                 fn: fn,
35045                 minWidth:250,
35046                 scope : scope,
35047                 prompt:true,
35048                 multiline: multiline,
35049                 modal : true
35050             });
35051             return this;
35052         },
35053
35054         /**
35055          * Button config that displays a single OK button
35056          * @type Object
35057          */
35058         OK : {ok:true},
35059         /**
35060          * Button config that displays Yes and No buttons
35061          * @type Object
35062          */
35063         YESNO : {yes:true, no:true},
35064         /**
35065          * Button config that displays OK and Cancel buttons
35066          * @type Object
35067          */
35068         OKCANCEL : {ok:true, cancel:true},
35069         /**
35070          * Button config that displays Yes, No and Cancel buttons
35071          * @type Object
35072          */
35073         YESNOCANCEL : {yes:true, no:true, cancel:true},
35074
35075         /**
35076          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35077          * @type Number
35078          */
35079         defaultTextHeight : 75,
35080         /**
35081          * The maximum width in pixels of the message box (defaults to 600)
35082          * @type Number
35083          */
35084         maxWidth : 600,
35085         /**
35086          * The minimum width in pixels of the message box (defaults to 100)
35087          * @type Number
35088          */
35089         minWidth : 100,
35090         /**
35091          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35092          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35093          * @type Number
35094          */
35095         minProgressWidth : 250,
35096         /**
35097          * An object containing the default button text strings that can be overriden for localized language support.
35098          * Supported properties are: ok, cancel, yes and no.
35099          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35100          * @type Object
35101          */
35102         buttonText : {
35103             ok : "OK",
35104             cancel : "Cancel",
35105             yes : "Yes",
35106             no : "No"
35107         }
35108     };
35109 }();
35110
35111 /**
35112  * Shorthand for {@link Roo.MessageBox}
35113  */
35114 Roo.Msg = Roo.MessageBox;/*
35115  * Based on:
35116  * Ext JS Library 1.1.1
35117  * Copyright(c) 2006-2007, Ext JS, LLC.
35118  *
35119  * Originally Released Under LGPL - original licence link has changed is not relivant.
35120  *
35121  * Fork - LGPL
35122  * <script type="text/javascript">
35123  */
35124 /**
35125  * @class Roo.QuickTips
35126  * Provides attractive and customizable tooltips for any element.
35127  * @static
35128  */
35129 Roo.QuickTips = function(){
35130     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35131     var ce, bd, xy, dd;
35132     var visible = false, disabled = true, inited = false;
35133     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35134     
35135     var onOver = function(e){
35136         if(disabled){
35137             return;
35138         }
35139         var t = e.getTarget();
35140         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35141             return;
35142         }
35143         if(ce && t == ce.el){
35144             clearTimeout(hideProc);
35145             return;
35146         }
35147         if(t && tagEls[t.id]){
35148             tagEls[t.id].el = t;
35149             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35150             return;
35151         }
35152         var ttp, et = Roo.fly(t);
35153         var ns = cfg.namespace;
35154         if(tm.interceptTitles && t.title){
35155             ttp = t.title;
35156             t.qtip = ttp;
35157             t.removeAttribute("title");
35158             e.preventDefault();
35159         }else{
35160             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35161         }
35162         if(ttp){
35163             showProc = show.defer(tm.showDelay, tm, [{
35164                 el: t, 
35165                 text: ttp.replace(/\\n/g,'<br/>'),
35166                 width: et.getAttributeNS(ns, cfg.width),
35167                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35168                 title: et.getAttributeNS(ns, cfg.title),
35169                     cls: et.getAttributeNS(ns, cfg.cls)
35170             }]);
35171         }
35172     };
35173     
35174     var onOut = function(e){
35175         clearTimeout(showProc);
35176         var t = e.getTarget();
35177         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35178             hideProc = setTimeout(hide, tm.hideDelay);
35179         }
35180     };
35181     
35182     var onMove = function(e){
35183         if(disabled){
35184             return;
35185         }
35186         xy = e.getXY();
35187         xy[1] += 18;
35188         if(tm.trackMouse && ce){
35189             el.setXY(xy);
35190         }
35191     };
35192     
35193     var onDown = function(e){
35194         clearTimeout(showProc);
35195         clearTimeout(hideProc);
35196         if(!e.within(el)){
35197             if(tm.hideOnClick){
35198                 hide();
35199                 tm.disable();
35200                 tm.enable.defer(100, tm);
35201             }
35202         }
35203     };
35204     
35205     var getPad = function(){
35206         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35207     };
35208
35209     var show = function(o){
35210         if(disabled){
35211             return;
35212         }
35213         clearTimeout(dismissProc);
35214         ce = o;
35215         if(removeCls){ // in case manually hidden
35216             el.removeClass(removeCls);
35217             removeCls = null;
35218         }
35219         if(ce.cls){
35220             el.addClass(ce.cls);
35221             removeCls = ce.cls;
35222         }
35223         if(ce.title){
35224             tipTitle.update(ce.title);
35225             tipTitle.show();
35226         }else{
35227             tipTitle.update('');
35228             tipTitle.hide();
35229         }
35230         el.dom.style.width  = tm.maxWidth+'px';
35231         //tipBody.dom.style.width = '';
35232         tipBodyText.update(o.text);
35233         var p = getPad(), w = ce.width;
35234         if(!w){
35235             var td = tipBodyText.dom;
35236             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35237             if(aw > tm.maxWidth){
35238                 w = tm.maxWidth;
35239             }else if(aw < tm.minWidth){
35240                 w = tm.minWidth;
35241             }else{
35242                 w = aw;
35243             }
35244         }
35245         //tipBody.setWidth(w);
35246         el.setWidth(parseInt(w, 10) + p);
35247         if(ce.autoHide === false){
35248             close.setDisplayed(true);
35249             if(dd){
35250                 dd.unlock();
35251             }
35252         }else{
35253             close.setDisplayed(false);
35254             if(dd){
35255                 dd.lock();
35256             }
35257         }
35258         if(xy){
35259             el.avoidY = xy[1]-18;
35260             el.setXY(xy);
35261         }
35262         if(tm.animate){
35263             el.setOpacity(.1);
35264             el.setStyle("visibility", "visible");
35265             el.fadeIn({callback: afterShow});
35266         }else{
35267             afterShow();
35268         }
35269     };
35270     
35271     var afterShow = function(){
35272         if(ce){
35273             el.show();
35274             esc.enable();
35275             if(tm.autoDismiss && ce.autoHide !== false){
35276                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35277             }
35278         }
35279     };
35280     
35281     var hide = function(noanim){
35282         clearTimeout(dismissProc);
35283         clearTimeout(hideProc);
35284         ce = null;
35285         if(el.isVisible()){
35286             esc.disable();
35287             if(noanim !== true && tm.animate){
35288                 el.fadeOut({callback: afterHide});
35289             }else{
35290                 afterHide();
35291             } 
35292         }
35293     };
35294     
35295     var afterHide = function(){
35296         el.hide();
35297         if(removeCls){
35298             el.removeClass(removeCls);
35299             removeCls = null;
35300         }
35301     };
35302     
35303     return {
35304         /**
35305         * @cfg {Number} minWidth
35306         * The minimum width of the quick tip (defaults to 40)
35307         */
35308        minWidth : 40,
35309         /**
35310         * @cfg {Number} maxWidth
35311         * The maximum width of the quick tip (defaults to 300)
35312         */
35313        maxWidth : 300,
35314         /**
35315         * @cfg {Boolean} interceptTitles
35316         * True to automatically use the element's DOM title value if available (defaults to false)
35317         */
35318        interceptTitles : false,
35319         /**
35320         * @cfg {Boolean} trackMouse
35321         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35322         */
35323        trackMouse : false,
35324         /**
35325         * @cfg {Boolean} hideOnClick
35326         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35327         */
35328        hideOnClick : true,
35329         /**
35330         * @cfg {Number} showDelay
35331         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35332         */
35333        showDelay : 500,
35334         /**
35335         * @cfg {Number} hideDelay
35336         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35337         */
35338        hideDelay : 200,
35339         /**
35340         * @cfg {Boolean} autoHide
35341         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35342         * Used in conjunction with hideDelay.
35343         */
35344        autoHide : true,
35345         /**
35346         * @cfg {Boolean}
35347         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35348         * (defaults to true).  Used in conjunction with autoDismissDelay.
35349         */
35350        autoDismiss : true,
35351         /**
35352         * @cfg {Number}
35353         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35354         */
35355        autoDismissDelay : 5000,
35356        /**
35357         * @cfg {Boolean} animate
35358         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35359         */
35360        animate : false,
35361
35362        /**
35363         * @cfg {String} title
35364         * Title text to display (defaults to '').  This can be any valid HTML markup.
35365         */
35366         title: '',
35367        /**
35368         * @cfg {String} text
35369         * Body text to display (defaults to '').  This can be any valid HTML markup.
35370         */
35371         text : '',
35372        /**
35373         * @cfg {String} cls
35374         * A CSS class to apply to the base quick tip element (defaults to '').
35375         */
35376         cls : '',
35377        /**
35378         * @cfg {Number} width
35379         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35380         * minWidth or maxWidth.
35381         */
35382         width : null,
35383
35384     /**
35385      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35386      * or display QuickTips in a page.
35387      */
35388        init : function(){
35389           tm = Roo.QuickTips;
35390           cfg = tm.tagConfig;
35391           if(!inited){
35392               if(!Roo.isReady){ // allow calling of init() before onReady
35393                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35394                   return;
35395               }
35396               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35397               el.fxDefaults = {stopFx: true};
35398               // maximum custom styling
35399               //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>');
35400               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>');              
35401               tipTitle = el.child('h3');
35402               tipTitle.enableDisplayMode("block");
35403               tipBody = el.child('div.x-tip-bd');
35404               tipBodyText = el.child('div.x-tip-bd-inner');
35405               //bdLeft = el.child('div.x-tip-bd-left');
35406               //bdRight = el.child('div.x-tip-bd-right');
35407               close = el.child('div.x-tip-close');
35408               close.enableDisplayMode("block");
35409               close.on("click", hide);
35410               var d = Roo.get(document);
35411               d.on("mousedown", onDown);
35412               d.on("mouseover", onOver);
35413               d.on("mouseout", onOut);
35414               d.on("mousemove", onMove);
35415               esc = d.addKeyListener(27, hide);
35416               esc.disable();
35417               if(Roo.dd.DD){
35418                   dd = el.initDD("default", null, {
35419                       onDrag : function(){
35420                           el.sync();  
35421                       }
35422                   });
35423                   dd.setHandleElId(tipTitle.id);
35424                   dd.lock();
35425               }
35426               inited = true;
35427           }
35428           this.enable(); 
35429        },
35430
35431     /**
35432      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35433      * are supported:
35434      * <pre>
35435 Property    Type                   Description
35436 ----------  ---------------------  ------------------------------------------------------------------------
35437 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35438      * </ul>
35439      * @param {Object} config The config object
35440      */
35441        register : function(config){
35442            var cs = config instanceof Array ? config : arguments;
35443            for(var i = 0, len = cs.length; i < len; i++) {
35444                var c = cs[i];
35445                var target = c.target;
35446                if(target){
35447                    if(target instanceof Array){
35448                        for(var j = 0, jlen = target.length; j < jlen; j++){
35449                            tagEls[target[j]] = c;
35450                        }
35451                    }else{
35452                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35453                    }
35454                }
35455            }
35456        },
35457
35458     /**
35459      * Removes this quick tip from its element and destroys it.
35460      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35461      */
35462        unregister : function(el){
35463            delete tagEls[Roo.id(el)];
35464        },
35465
35466     /**
35467      * Enable this quick tip.
35468      */
35469        enable : function(){
35470            if(inited && disabled){
35471                locks.pop();
35472                if(locks.length < 1){
35473                    disabled = false;
35474                }
35475            }
35476        },
35477
35478     /**
35479      * Disable this quick tip.
35480      */
35481        disable : function(){
35482           disabled = true;
35483           clearTimeout(showProc);
35484           clearTimeout(hideProc);
35485           clearTimeout(dismissProc);
35486           if(ce){
35487               hide(true);
35488           }
35489           locks.push(1);
35490        },
35491
35492     /**
35493      * Returns true if the quick tip is enabled, else false.
35494      */
35495        isEnabled : function(){
35496             return !disabled;
35497        },
35498
35499         // private
35500        tagConfig : {
35501            namespace : "roo", // was ext?? this may break..
35502            alt_namespace : "ext",
35503            attribute : "qtip",
35504            width : "width",
35505            target : "target",
35506            title : "qtitle",
35507            hide : "hide",
35508            cls : "qclass"
35509        }
35510    };
35511 }();
35512
35513 // backwards compat
35514 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35515  * Based on:
35516  * Ext JS Library 1.1.1
35517  * Copyright(c) 2006-2007, Ext JS, LLC.
35518  *
35519  * Originally Released Under LGPL - original licence link has changed is not relivant.
35520  *
35521  * Fork - LGPL
35522  * <script type="text/javascript">
35523  */
35524  
35525
35526 /**
35527  * @class Roo.tree.TreePanel
35528  * @extends Roo.data.Tree
35529  * @cfg {Roo.tree.TreeNode} root The root node
35530  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35531  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35532  * @cfg {Boolean} enableDD true to enable drag and drop
35533  * @cfg {Boolean} enableDrag true to enable just drag
35534  * @cfg {Boolean} enableDrop true to enable just drop
35535  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35536  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35537  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35538  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35539  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35540  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35541  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35542  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35543  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35544  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35545  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35546  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35547  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35548  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35549  * @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>
35550  * @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>
35551  * 
35552  * @constructor
35553  * @param {String/HTMLElement/Element} el The container element
35554  * @param {Object} config
35555  */
35556 Roo.tree.TreePanel = function(el, config){
35557     var root = false;
35558     var loader = false;
35559     if (config.root) {
35560         root = config.root;
35561         delete config.root;
35562     }
35563     if (config.loader) {
35564         loader = config.loader;
35565         delete config.loader;
35566     }
35567     
35568     Roo.apply(this, config);
35569     Roo.tree.TreePanel.superclass.constructor.call(this);
35570     this.el = Roo.get(el);
35571     this.el.addClass('x-tree');
35572     //console.log(root);
35573     if (root) {
35574         this.setRootNode( Roo.factory(root, Roo.tree));
35575     }
35576     if (loader) {
35577         this.loader = Roo.factory(loader, Roo.tree);
35578     }
35579    /**
35580     * Read-only. The id of the container element becomes this TreePanel's id.
35581     */
35582     this.id = this.el.id;
35583     this.addEvents({
35584         /**
35585         * @event beforeload
35586         * Fires before a node is loaded, return false to cancel
35587         * @param {Node} node The node being loaded
35588         */
35589         "beforeload" : true,
35590         /**
35591         * @event load
35592         * Fires when a node is loaded
35593         * @param {Node} node The node that was loaded
35594         */
35595         "load" : true,
35596         /**
35597         * @event textchange
35598         * Fires when the text for a node is changed
35599         * @param {Node} node The node
35600         * @param {String} text The new text
35601         * @param {String} oldText The old text
35602         */
35603         "textchange" : true,
35604         /**
35605         * @event beforeexpand
35606         * Fires before a node is expanded, return false to cancel.
35607         * @param {Node} node The node
35608         * @param {Boolean} deep
35609         * @param {Boolean} anim
35610         */
35611         "beforeexpand" : true,
35612         /**
35613         * @event beforecollapse
35614         * Fires before a node is collapsed, return false to cancel.
35615         * @param {Node} node The node
35616         * @param {Boolean} deep
35617         * @param {Boolean} anim
35618         */
35619         "beforecollapse" : true,
35620         /**
35621         * @event expand
35622         * Fires when a node is expanded
35623         * @param {Node} node The node
35624         */
35625         "expand" : true,
35626         /**
35627         * @event disabledchange
35628         * Fires when the disabled status of a node changes
35629         * @param {Node} node The node
35630         * @param {Boolean} disabled
35631         */
35632         "disabledchange" : true,
35633         /**
35634         * @event collapse
35635         * Fires when a node is collapsed
35636         * @param {Node} node The node
35637         */
35638         "collapse" : true,
35639         /**
35640         * @event beforeclick
35641         * Fires before click processing on a node. Return false to cancel the default action.
35642         * @param {Node} node The node
35643         * @param {Roo.EventObject} e The event object
35644         */
35645         "beforeclick":true,
35646         /**
35647         * @event checkchange
35648         * Fires when a node with a checkbox's checked property changes
35649         * @param {Node} this This node
35650         * @param {Boolean} checked
35651         */
35652         "checkchange":true,
35653         /**
35654         * @event click
35655         * Fires when a node is clicked
35656         * @param {Node} node The node
35657         * @param {Roo.EventObject} e The event object
35658         */
35659         "click":true,
35660         /**
35661         * @event dblclick
35662         * Fires when a node is double clicked
35663         * @param {Node} node The node
35664         * @param {Roo.EventObject} e The event object
35665         */
35666         "dblclick":true,
35667         /**
35668         * @event contextmenu
35669         * Fires when a node is right clicked
35670         * @param {Node} node The node
35671         * @param {Roo.EventObject} e The event object
35672         */
35673         "contextmenu":true,
35674         /**
35675         * @event beforechildrenrendered
35676         * Fires right before the child nodes for a node are rendered
35677         * @param {Node} node The node
35678         */
35679         "beforechildrenrendered":true,
35680         /**
35681         * @event startdrag
35682         * Fires when a node starts being dragged
35683         * @param {Roo.tree.TreePanel} this
35684         * @param {Roo.tree.TreeNode} node
35685         * @param {event} e The raw browser event
35686         */ 
35687        "startdrag" : true,
35688        /**
35689         * @event enddrag
35690         * Fires when a drag operation is complete
35691         * @param {Roo.tree.TreePanel} this
35692         * @param {Roo.tree.TreeNode} node
35693         * @param {event} e The raw browser event
35694         */
35695        "enddrag" : true,
35696        /**
35697         * @event dragdrop
35698         * Fires when a dragged node is dropped on a valid DD target
35699         * @param {Roo.tree.TreePanel} this
35700         * @param {Roo.tree.TreeNode} node
35701         * @param {DD} dd The dd it was dropped on
35702         * @param {event} e The raw browser event
35703         */
35704        "dragdrop" : true,
35705        /**
35706         * @event beforenodedrop
35707         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35708         * passed to handlers has the following properties:<br />
35709         * <ul style="padding:5px;padding-left:16px;">
35710         * <li>tree - The TreePanel</li>
35711         * <li>target - The node being targeted for the drop</li>
35712         * <li>data - The drag data from the drag source</li>
35713         * <li>point - The point of the drop - append, above or below</li>
35714         * <li>source - The drag source</li>
35715         * <li>rawEvent - Raw mouse event</li>
35716         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35717         * to be inserted by setting them on this object.</li>
35718         * <li>cancel - Set this to true to cancel the drop.</li>
35719         * </ul>
35720         * @param {Object} dropEvent
35721         */
35722        "beforenodedrop" : true,
35723        /**
35724         * @event nodedrop
35725         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35726         * passed to handlers has the following properties:<br />
35727         * <ul style="padding:5px;padding-left:16px;">
35728         * <li>tree - The TreePanel</li>
35729         * <li>target - The node being targeted for the drop</li>
35730         * <li>data - The drag data from the drag source</li>
35731         * <li>point - The point of the drop - append, above or below</li>
35732         * <li>source - The drag source</li>
35733         * <li>rawEvent - Raw mouse event</li>
35734         * <li>dropNode - Dropped node(s).</li>
35735         * </ul>
35736         * @param {Object} dropEvent
35737         */
35738        "nodedrop" : true,
35739         /**
35740         * @event nodedragover
35741         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35742         * passed to handlers has the following properties:<br />
35743         * <ul style="padding:5px;padding-left:16px;">
35744         * <li>tree - The TreePanel</li>
35745         * <li>target - The node being targeted for the drop</li>
35746         * <li>data - The drag data from the drag source</li>
35747         * <li>point - The point of the drop - append, above or below</li>
35748         * <li>source - The drag source</li>
35749         * <li>rawEvent - Raw mouse event</li>
35750         * <li>dropNode - Drop node(s) provided by the source.</li>
35751         * <li>cancel - Set this to true to signal drop not allowed.</li>
35752         * </ul>
35753         * @param {Object} dragOverEvent
35754         */
35755        "nodedragover" : true,
35756        /**
35757         * @event appendnode
35758         * Fires when append node to the tree
35759         * @param {Roo.tree.TreePanel} this
35760         * @param {Roo.tree.TreeNode} node
35761         * @param {Number} index The index of the newly appended node
35762         */
35763        "appendnode" : true
35764         
35765     });
35766     if(this.singleExpand){
35767        this.on("beforeexpand", this.restrictExpand, this);
35768     }
35769     if (this.editor) {
35770         this.editor.tree = this;
35771         this.editor = Roo.factory(this.editor, Roo.tree);
35772     }
35773     
35774     if (this.selModel) {
35775         this.selModel = Roo.factory(this.selModel, Roo.tree);
35776     }
35777    
35778 };
35779 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35780     rootVisible : true,
35781     animate: Roo.enableFx,
35782     lines : true,
35783     enableDD : false,
35784     hlDrop : Roo.enableFx,
35785   
35786     renderer: false,
35787     
35788     rendererTip: false,
35789     // private
35790     restrictExpand : function(node){
35791         var p = node.parentNode;
35792         if(p){
35793             if(p.expandedChild && p.expandedChild.parentNode == p){
35794                 p.expandedChild.collapse();
35795             }
35796             p.expandedChild = node;
35797         }
35798     },
35799
35800     // private override
35801     setRootNode : function(node){
35802         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35803         if(!this.rootVisible){
35804             node.ui = new Roo.tree.RootTreeNodeUI(node);
35805         }
35806         return node;
35807     },
35808
35809     /**
35810      * Returns the container element for this TreePanel
35811      */
35812     getEl : function(){
35813         return this.el;
35814     },
35815
35816     /**
35817      * Returns the default TreeLoader for this TreePanel
35818      */
35819     getLoader : function(){
35820         return this.loader;
35821     },
35822
35823     /**
35824      * Expand all nodes
35825      */
35826     expandAll : function(){
35827         this.root.expand(true);
35828     },
35829
35830     /**
35831      * Collapse all nodes
35832      */
35833     collapseAll : function(){
35834         this.root.collapse(true);
35835     },
35836
35837     /**
35838      * Returns the selection model used by this TreePanel
35839      */
35840     getSelectionModel : function(){
35841         if(!this.selModel){
35842             this.selModel = new Roo.tree.DefaultSelectionModel();
35843         }
35844         return this.selModel;
35845     },
35846
35847     /**
35848      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35849      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35850      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35851      * @return {Array}
35852      */
35853     getChecked : function(a, startNode){
35854         startNode = startNode || this.root;
35855         var r = [];
35856         var f = function(){
35857             if(this.attributes.checked){
35858                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35859             }
35860         }
35861         startNode.cascade(f);
35862         return r;
35863     },
35864
35865     /**
35866      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35867      * @param {String} path
35868      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35869      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35870      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35871      */
35872     expandPath : function(path, attr, callback){
35873         attr = attr || "id";
35874         var keys = path.split(this.pathSeparator);
35875         var curNode = this.root;
35876         if(curNode.attributes[attr] != keys[1]){ // invalid root
35877             if(callback){
35878                 callback(false, null);
35879             }
35880             return;
35881         }
35882         var index = 1;
35883         var f = function(){
35884             if(++index == keys.length){
35885                 if(callback){
35886                     callback(true, curNode);
35887                 }
35888                 return;
35889             }
35890             var c = curNode.findChild(attr, keys[index]);
35891             if(!c){
35892                 if(callback){
35893                     callback(false, curNode);
35894                 }
35895                 return;
35896             }
35897             curNode = c;
35898             c.expand(false, false, f);
35899         };
35900         curNode.expand(false, false, f);
35901     },
35902
35903     /**
35904      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35905      * @param {String} path
35906      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35907      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35908      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35909      */
35910     selectPath : function(path, attr, callback){
35911         attr = attr || "id";
35912         var keys = path.split(this.pathSeparator);
35913         var v = keys.pop();
35914         if(keys.length > 0){
35915             var f = function(success, node){
35916                 if(success && node){
35917                     var n = node.findChild(attr, v);
35918                     if(n){
35919                         n.select();
35920                         if(callback){
35921                             callback(true, n);
35922                         }
35923                     }else if(callback){
35924                         callback(false, n);
35925                     }
35926                 }else{
35927                     if(callback){
35928                         callback(false, n);
35929                     }
35930                 }
35931             };
35932             this.expandPath(keys.join(this.pathSeparator), attr, f);
35933         }else{
35934             this.root.select();
35935             if(callback){
35936                 callback(true, this.root);
35937             }
35938         }
35939     },
35940
35941     getTreeEl : function(){
35942         return this.el;
35943     },
35944
35945     /**
35946      * Trigger rendering of this TreePanel
35947      */
35948     render : function(){
35949         if (this.innerCt) {
35950             return this; // stop it rendering more than once!!
35951         }
35952         
35953         this.innerCt = this.el.createChild({tag:"ul",
35954                cls:"x-tree-root-ct " +
35955                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35956
35957         if(this.containerScroll){
35958             Roo.dd.ScrollManager.register(this.el);
35959         }
35960         if((this.enableDD || this.enableDrop) && !this.dropZone){
35961            /**
35962             * The dropZone used by this tree if drop is enabled
35963             * @type Roo.tree.TreeDropZone
35964             */
35965              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35966                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35967            });
35968         }
35969         if((this.enableDD || this.enableDrag) && !this.dragZone){
35970            /**
35971             * The dragZone used by this tree if drag is enabled
35972             * @type Roo.tree.TreeDragZone
35973             */
35974             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35975                ddGroup: this.ddGroup || "TreeDD",
35976                scroll: this.ddScroll
35977            });
35978         }
35979         this.getSelectionModel().init(this);
35980         if (!this.root) {
35981             Roo.log("ROOT not set in tree");
35982             return this;
35983         }
35984         this.root.render();
35985         if(!this.rootVisible){
35986             this.root.renderChildren();
35987         }
35988         return this;
35989     }
35990 });/*
35991  * Based on:
35992  * Ext JS Library 1.1.1
35993  * Copyright(c) 2006-2007, Ext JS, LLC.
35994  *
35995  * Originally Released Under LGPL - original licence link has changed is not relivant.
35996  *
35997  * Fork - LGPL
35998  * <script type="text/javascript">
35999  */
36000  
36001
36002 /**
36003  * @class Roo.tree.DefaultSelectionModel
36004  * @extends Roo.util.Observable
36005  * The default single selection for a TreePanel.
36006  * @param {Object} cfg Configuration
36007  */
36008 Roo.tree.DefaultSelectionModel = function(cfg){
36009    this.selNode = null;
36010    
36011    
36012    
36013    this.addEvents({
36014        /**
36015         * @event selectionchange
36016         * Fires when the selected node changes
36017         * @param {DefaultSelectionModel} this
36018         * @param {TreeNode} node the new selection
36019         */
36020        "selectionchange" : true,
36021
36022        /**
36023         * @event beforeselect
36024         * Fires before the selected node changes, return false to cancel the change
36025         * @param {DefaultSelectionModel} this
36026         * @param {TreeNode} node the new selection
36027         * @param {TreeNode} node the old selection
36028         */
36029        "beforeselect" : true
36030    });
36031    
36032     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36033 };
36034
36035 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36036     init : function(tree){
36037         this.tree = tree;
36038         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36039         tree.on("click", this.onNodeClick, this);
36040     },
36041     
36042     onNodeClick : function(node, e){
36043         if (e.ctrlKey && this.selNode == node)  {
36044             this.unselect(node);
36045             return;
36046         }
36047         this.select(node);
36048     },
36049     
36050     /**
36051      * Select a node.
36052      * @param {TreeNode} node The node to select
36053      * @return {TreeNode} The selected node
36054      */
36055     select : function(node){
36056         var last = this.selNode;
36057         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36058             if(last){
36059                 last.ui.onSelectedChange(false);
36060             }
36061             this.selNode = node;
36062             node.ui.onSelectedChange(true);
36063             this.fireEvent("selectionchange", this, node, last);
36064         }
36065         return node;
36066     },
36067     
36068     /**
36069      * Deselect a node.
36070      * @param {TreeNode} node The node to unselect
36071      */
36072     unselect : function(node){
36073         if(this.selNode == node){
36074             this.clearSelections();
36075         }    
36076     },
36077     
36078     /**
36079      * Clear all selections
36080      */
36081     clearSelections : function(){
36082         var n = this.selNode;
36083         if(n){
36084             n.ui.onSelectedChange(false);
36085             this.selNode = null;
36086             this.fireEvent("selectionchange", this, null);
36087         }
36088         return n;
36089     },
36090     
36091     /**
36092      * Get the selected node
36093      * @return {TreeNode} The selected node
36094      */
36095     getSelectedNode : function(){
36096         return this.selNode;    
36097     },
36098     
36099     /**
36100      * Returns true if the node is selected
36101      * @param {TreeNode} node The node to check
36102      * @return {Boolean}
36103      */
36104     isSelected : function(node){
36105         return this.selNode == node;  
36106     },
36107
36108     /**
36109      * Selects the node above the selected node in the tree, intelligently walking the nodes
36110      * @return TreeNode The new selection
36111      */
36112     selectPrevious : function(){
36113         var s = this.selNode || this.lastSelNode;
36114         if(!s){
36115             return null;
36116         }
36117         var ps = s.previousSibling;
36118         if(ps){
36119             if(!ps.isExpanded() || ps.childNodes.length < 1){
36120                 return this.select(ps);
36121             } else{
36122                 var lc = ps.lastChild;
36123                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36124                     lc = lc.lastChild;
36125                 }
36126                 return this.select(lc);
36127             }
36128         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36129             return this.select(s.parentNode);
36130         }
36131         return null;
36132     },
36133
36134     /**
36135      * Selects the node above the selected node in the tree, intelligently walking the nodes
36136      * @return TreeNode The new selection
36137      */
36138     selectNext : function(){
36139         var s = this.selNode || this.lastSelNode;
36140         if(!s){
36141             return null;
36142         }
36143         if(s.firstChild && s.isExpanded()){
36144              return this.select(s.firstChild);
36145          }else if(s.nextSibling){
36146              return this.select(s.nextSibling);
36147          }else if(s.parentNode){
36148             var newS = null;
36149             s.parentNode.bubble(function(){
36150                 if(this.nextSibling){
36151                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36152                     return false;
36153                 }
36154             });
36155             return newS;
36156          }
36157         return null;
36158     },
36159
36160     onKeyDown : function(e){
36161         var s = this.selNode || this.lastSelNode;
36162         // undesirable, but required
36163         var sm = this;
36164         if(!s){
36165             return;
36166         }
36167         var k = e.getKey();
36168         switch(k){
36169              case e.DOWN:
36170                  e.stopEvent();
36171                  this.selectNext();
36172              break;
36173              case e.UP:
36174                  e.stopEvent();
36175                  this.selectPrevious();
36176              break;
36177              case e.RIGHT:
36178                  e.preventDefault();
36179                  if(s.hasChildNodes()){
36180                      if(!s.isExpanded()){
36181                          s.expand();
36182                      }else if(s.firstChild){
36183                          this.select(s.firstChild, e);
36184                      }
36185                  }
36186              break;
36187              case e.LEFT:
36188                  e.preventDefault();
36189                  if(s.hasChildNodes() && s.isExpanded()){
36190                      s.collapse();
36191                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36192                      this.select(s.parentNode, e);
36193                  }
36194              break;
36195         };
36196     }
36197 });
36198
36199 /**
36200  * @class Roo.tree.MultiSelectionModel
36201  * @extends Roo.util.Observable
36202  * Multi selection for a TreePanel.
36203  * @param {Object} cfg Configuration
36204  */
36205 Roo.tree.MultiSelectionModel = function(){
36206    this.selNodes = [];
36207    this.selMap = {};
36208    this.addEvents({
36209        /**
36210         * @event selectionchange
36211         * Fires when the selected nodes change
36212         * @param {MultiSelectionModel} this
36213         * @param {Array} nodes Array of the selected nodes
36214         */
36215        "selectionchange" : true
36216    });
36217    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36218    
36219 };
36220
36221 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36222     init : function(tree){
36223         this.tree = tree;
36224         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36225         tree.on("click", this.onNodeClick, this);
36226     },
36227     
36228     onNodeClick : function(node, e){
36229         this.select(node, e, e.ctrlKey);
36230     },
36231     
36232     /**
36233      * Select a node.
36234      * @param {TreeNode} node The node to select
36235      * @param {EventObject} e (optional) An event associated with the selection
36236      * @param {Boolean} keepExisting True to retain existing selections
36237      * @return {TreeNode} The selected node
36238      */
36239     select : function(node, e, keepExisting){
36240         if(keepExisting !== true){
36241             this.clearSelections(true);
36242         }
36243         if(this.isSelected(node)){
36244             this.lastSelNode = node;
36245             return node;
36246         }
36247         this.selNodes.push(node);
36248         this.selMap[node.id] = node;
36249         this.lastSelNode = node;
36250         node.ui.onSelectedChange(true);
36251         this.fireEvent("selectionchange", this, this.selNodes);
36252         return node;
36253     },
36254     
36255     /**
36256      * Deselect a node.
36257      * @param {TreeNode} node The node to unselect
36258      */
36259     unselect : function(node){
36260         if(this.selMap[node.id]){
36261             node.ui.onSelectedChange(false);
36262             var sn = this.selNodes;
36263             var index = -1;
36264             if(sn.indexOf){
36265                 index = sn.indexOf(node);
36266             }else{
36267                 for(var i = 0, len = sn.length; i < len; i++){
36268                     if(sn[i] == node){
36269                         index = i;
36270                         break;
36271                     }
36272                 }
36273             }
36274             if(index != -1){
36275                 this.selNodes.splice(index, 1);
36276             }
36277             delete this.selMap[node.id];
36278             this.fireEvent("selectionchange", this, this.selNodes);
36279         }
36280     },
36281     
36282     /**
36283      * Clear all selections
36284      */
36285     clearSelections : function(suppressEvent){
36286         var sn = this.selNodes;
36287         if(sn.length > 0){
36288             for(var i = 0, len = sn.length; i < len; i++){
36289                 sn[i].ui.onSelectedChange(false);
36290             }
36291             this.selNodes = [];
36292             this.selMap = {};
36293             if(suppressEvent !== true){
36294                 this.fireEvent("selectionchange", this, this.selNodes);
36295             }
36296         }
36297     },
36298     
36299     /**
36300      * Returns true if the node is selected
36301      * @param {TreeNode} node The node to check
36302      * @return {Boolean}
36303      */
36304     isSelected : function(node){
36305         return this.selMap[node.id] ? true : false;  
36306     },
36307     
36308     /**
36309      * Returns an array of the selected nodes
36310      * @return {Array}
36311      */
36312     getSelectedNodes : function(){
36313         return this.selNodes;    
36314     },
36315
36316     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36317
36318     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36319
36320     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36321 });/*
36322  * Based on:
36323  * Ext JS Library 1.1.1
36324  * Copyright(c) 2006-2007, Ext JS, LLC.
36325  *
36326  * Originally Released Under LGPL - original licence link has changed is not relivant.
36327  *
36328  * Fork - LGPL
36329  * <script type="text/javascript">
36330  */
36331  
36332 /**
36333  * @class Roo.tree.TreeNode
36334  * @extends Roo.data.Node
36335  * @cfg {String} text The text for this node
36336  * @cfg {Boolean} expanded true to start the node expanded
36337  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36338  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36339  * @cfg {Boolean} disabled true to start the node disabled
36340  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36341  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36342  * @cfg {String} cls A css class to be added to the node
36343  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36344  * @cfg {String} href URL of the link used for the node (defaults to #)
36345  * @cfg {String} hrefTarget target frame for the link
36346  * @cfg {String} qtip An Ext QuickTip for the node
36347  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36348  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36349  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36350  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36351  * (defaults to undefined with no checkbox rendered)
36352  * @constructor
36353  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36354  */
36355 Roo.tree.TreeNode = function(attributes){
36356     attributes = attributes || {};
36357     if(typeof attributes == "string"){
36358         attributes = {text: attributes};
36359     }
36360     this.childrenRendered = false;
36361     this.rendered = false;
36362     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36363     this.expanded = attributes.expanded === true;
36364     this.isTarget = attributes.isTarget !== false;
36365     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36366     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36367
36368     /**
36369      * Read-only. The text for this node. To change it use setText().
36370      * @type String
36371      */
36372     this.text = attributes.text;
36373     /**
36374      * True if this node is disabled.
36375      * @type Boolean
36376      */
36377     this.disabled = attributes.disabled === true;
36378
36379     this.addEvents({
36380         /**
36381         * @event textchange
36382         * Fires when the text for this node is changed
36383         * @param {Node} this This node
36384         * @param {String} text The new text
36385         * @param {String} oldText The old text
36386         */
36387         "textchange" : true,
36388         /**
36389         * @event beforeexpand
36390         * Fires before this node is expanded, return false to cancel.
36391         * @param {Node} this This node
36392         * @param {Boolean} deep
36393         * @param {Boolean} anim
36394         */
36395         "beforeexpand" : true,
36396         /**
36397         * @event beforecollapse
36398         * Fires before this node is collapsed, return false to cancel.
36399         * @param {Node} this This node
36400         * @param {Boolean} deep
36401         * @param {Boolean} anim
36402         */
36403         "beforecollapse" : true,
36404         /**
36405         * @event expand
36406         * Fires when this node is expanded
36407         * @param {Node} this This node
36408         */
36409         "expand" : true,
36410         /**
36411         * @event disabledchange
36412         * Fires when the disabled status of this node changes
36413         * @param {Node} this This node
36414         * @param {Boolean} disabled
36415         */
36416         "disabledchange" : true,
36417         /**
36418         * @event collapse
36419         * Fires when this node is collapsed
36420         * @param {Node} this This node
36421         */
36422         "collapse" : true,
36423         /**
36424         * @event beforeclick
36425         * Fires before click processing. Return false to cancel the default action.
36426         * @param {Node} this This node
36427         * @param {Roo.EventObject} e The event object
36428         */
36429         "beforeclick":true,
36430         /**
36431         * @event checkchange
36432         * Fires when a node with a checkbox's checked property changes
36433         * @param {Node} this This node
36434         * @param {Boolean} checked
36435         */
36436         "checkchange":true,
36437         /**
36438         * @event click
36439         * Fires when this node is clicked
36440         * @param {Node} this This node
36441         * @param {Roo.EventObject} e The event object
36442         */
36443         "click":true,
36444         /**
36445         * @event dblclick
36446         * Fires when this node is double clicked
36447         * @param {Node} this This node
36448         * @param {Roo.EventObject} e The event object
36449         */
36450         "dblclick":true,
36451         /**
36452         * @event contextmenu
36453         * Fires when this node is right clicked
36454         * @param {Node} this This node
36455         * @param {Roo.EventObject} e The event object
36456         */
36457         "contextmenu":true,
36458         /**
36459         * @event beforechildrenrendered
36460         * Fires right before the child nodes for this node are rendered
36461         * @param {Node} this This node
36462         */
36463         "beforechildrenrendered":true
36464     });
36465
36466     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36467
36468     /**
36469      * Read-only. The UI for this node
36470      * @type TreeNodeUI
36471      */
36472     this.ui = new uiClass(this);
36473     
36474     // finally support items[]
36475     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36476         return;
36477     }
36478     
36479     
36480     Roo.each(this.attributes.items, function(c) {
36481         this.appendChild(Roo.factory(c,Roo.Tree));
36482     }, this);
36483     delete this.attributes.items;
36484     
36485     
36486     
36487 };
36488 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36489     preventHScroll: true,
36490     /**
36491      * Returns true if this node is expanded
36492      * @return {Boolean}
36493      */
36494     isExpanded : function(){
36495         return this.expanded;
36496     },
36497
36498     /**
36499      * Returns the UI object for this node
36500      * @return {TreeNodeUI}
36501      */
36502     getUI : function(){
36503         return this.ui;
36504     },
36505
36506     // private override
36507     setFirstChild : function(node){
36508         var of = this.firstChild;
36509         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36510         if(this.childrenRendered && of && node != of){
36511             of.renderIndent(true, true);
36512         }
36513         if(this.rendered){
36514             this.renderIndent(true, true);
36515         }
36516     },
36517
36518     // private override
36519     setLastChild : function(node){
36520         var ol = this.lastChild;
36521         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36522         if(this.childrenRendered && ol && node != ol){
36523             ol.renderIndent(true, true);
36524         }
36525         if(this.rendered){
36526             this.renderIndent(true, true);
36527         }
36528     },
36529
36530     // these methods are overridden to provide lazy rendering support
36531     // private override
36532     appendChild : function()
36533     {
36534         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36535         if(node && this.childrenRendered){
36536             node.render();
36537         }
36538         this.ui.updateExpandIcon();
36539         return node;
36540     },
36541
36542     // private override
36543     removeChild : function(node){
36544         this.ownerTree.getSelectionModel().unselect(node);
36545         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36546         // if it's been rendered remove dom node
36547         if(this.childrenRendered){
36548             node.ui.remove();
36549         }
36550         if(this.childNodes.length < 1){
36551             this.collapse(false, false);
36552         }else{
36553             this.ui.updateExpandIcon();
36554         }
36555         if(!this.firstChild) {
36556             this.childrenRendered = false;
36557         }
36558         return node;
36559     },
36560
36561     // private override
36562     insertBefore : function(node, refNode){
36563         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36564         if(newNode && refNode && this.childrenRendered){
36565             node.render();
36566         }
36567         this.ui.updateExpandIcon();
36568         return newNode;
36569     },
36570
36571     /**
36572      * Sets the text for this node
36573      * @param {String} text
36574      */
36575     setText : function(text){
36576         var oldText = this.text;
36577         this.text = text;
36578         this.attributes.text = text;
36579         if(this.rendered){ // event without subscribing
36580             this.ui.onTextChange(this, text, oldText);
36581         }
36582         this.fireEvent("textchange", this, text, oldText);
36583     },
36584
36585     /**
36586      * Triggers selection of this node
36587      */
36588     select : function(){
36589         this.getOwnerTree().getSelectionModel().select(this);
36590     },
36591
36592     /**
36593      * Triggers deselection of this node
36594      */
36595     unselect : function(){
36596         this.getOwnerTree().getSelectionModel().unselect(this);
36597     },
36598
36599     /**
36600      * Returns true if this node is selected
36601      * @return {Boolean}
36602      */
36603     isSelected : function(){
36604         return this.getOwnerTree().getSelectionModel().isSelected(this);
36605     },
36606
36607     /**
36608      * Expand this node.
36609      * @param {Boolean} deep (optional) True to expand all children as well
36610      * @param {Boolean} anim (optional) false to cancel the default animation
36611      * @param {Function} callback (optional) A callback to be called when
36612      * expanding this node completes (does not wait for deep expand to complete).
36613      * Called with 1 parameter, this node.
36614      */
36615     expand : function(deep, anim, callback){
36616         if(!this.expanded){
36617             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36618                 return;
36619             }
36620             if(!this.childrenRendered){
36621                 this.renderChildren();
36622             }
36623             this.expanded = true;
36624             
36625             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36626                 this.ui.animExpand(function(){
36627                     this.fireEvent("expand", this);
36628                     if(typeof callback == "function"){
36629                         callback(this);
36630                     }
36631                     if(deep === true){
36632                         this.expandChildNodes(true);
36633                     }
36634                 }.createDelegate(this));
36635                 return;
36636             }else{
36637                 this.ui.expand();
36638                 this.fireEvent("expand", this);
36639                 if(typeof callback == "function"){
36640                     callback(this);
36641                 }
36642             }
36643         }else{
36644            if(typeof callback == "function"){
36645                callback(this);
36646            }
36647         }
36648         if(deep === true){
36649             this.expandChildNodes(true);
36650         }
36651     },
36652
36653     isHiddenRoot : function(){
36654         return this.isRoot && !this.getOwnerTree().rootVisible;
36655     },
36656
36657     /**
36658      * Collapse this node.
36659      * @param {Boolean} deep (optional) True to collapse all children as well
36660      * @param {Boolean} anim (optional) false to cancel the default animation
36661      */
36662     collapse : function(deep, anim){
36663         if(this.expanded && !this.isHiddenRoot()){
36664             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36665                 return;
36666             }
36667             this.expanded = false;
36668             if((this.getOwnerTree().animate && anim !== false) || anim){
36669                 this.ui.animCollapse(function(){
36670                     this.fireEvent("collapse", this);
36671                     if(deep === true){
36672                         this.collapseChildNodes(true);
36673                     }
36674                 }.createDelegate(this));
36675                 return;
36676             }else{
36677                 this.ui.collapse();
36678                 this.fireEvent("collapse", this);
36679             }
36680         }
36681         if(deep === true){
36682             var cs = this.childNodes;
36683             for(var i = 0, len = cs.length; i < len; i++) {
36684                 cs[i].collapse(true, false);
36685             }
36686         }
36687     },
36688
36689     // private
36690     delayedExpand : function(delay){
36691         if(!this.expandProcId){
36692             this.expandProcId = this.expand.defer(delay, this);
36693         }
36694     },
36695
36696     // private
36697     cancelExpand : function(){
36698         if(this.expandProcId){
36699             clearTimeout(this.expandProcId);
36700         }
36701         this.expandProcId = false;
36702     },
36703
36704     /**
36705      * Toggles expanded/collapsed state of the node
36706      */
36707     toggle : function(){
36708         if(this.expanded){
36709             this.collapse();
36710         }else{
36711             this.expand();
36712         }
36713     },
36714
36715     /**
36716      * Ensures all parent nodes are expanded
36717      */
36718     ensureVisible : function(callback){
36719         var tree = this.getOwnerTree();
36720         tree.expandPath(this.parentNode.getPath(), false, function(){
36721             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36722             Roo.callback(callback);
36723         }.createDelegate(this));
36724     },
36725
36726     /**
36727      * Expand all child nodes
36728      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36729      */
36730     expandChildNodes : function(deep){
36731         var cs = this.childNodes;
36732         for(var i = 0, len = cs.length; i < len; i++) {
36733                 cs[i].expand(deep);
36734         }
36735     },
36736
36737     /**
36738      * Collapse all child nodes
36739      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36740      */
36741     collapseChildNodes : function(deep){
36742         var cs = this.childNodes;
36743         for(var i = 0, len = cs.length; i < len; i++) {
36744                 cs[i].collapse(deep);
36745         }
36746     },
36747
36748     /**
36749      * Disables this node
36750      */
36751     disable : function(){
36752         this.disabled = true;
36753         this.unselect();
36754         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36755             this.ui.onDisableChange(this, true);
36756         }
36757         this.fireEvent("disabledchange", this, true);
36758     },
36759
36760     /**
36761      * Enables this node
36762      */
36763     enable : function(){
36764         this.disabled = false;
36765         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36766             this.ui.onDisableChange(this, false);
36767         }
36768         this.fireEvent("disabledchange", this, false);
36769     },
36770
36771     // private
36772     renderChildren : function(suppressEvent){
36773         if(suppressEvent !== false){
36774             this.fireEvent("beforechildrenrendered", this);
36775         }
36776         var cs = this.childNodes;
36777         for(var i = 0, len = cs.length; i < len; i++){
36778             cs[i].render(true);
36779         }
36780         this.childrenRendered = true;
36781     },
36782
36783     // private
36784     sort : function(fn, scope){
36785         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36786         if(this.childrenRendered){
36787             var cs = this.childNodes;
36788             for(var i = 0, len = cs.length; i < len; i++){
36789                 cs[i].render(true);
36790             }
36791         }
36792     },
36793
36794     // private
36795     render : function(bulkRender){
36796         this.ui.render(bulkRender);
36797         if(!this.rendered){
36798             this.rendered = true;
36799             if(this.expanded){
36800                 this.expanded = false;
36801                 this.expand(false, false);
36802             }
36803         }
36804     },
36805
36806     // private
36807     renderIndent : function(deep, refresh){
36808         if(refresh){
36809             this.ui.childIndent = null;
36810         }
36811         this.ui.renderIndent();
36812         if(deep === true && this.childrenRendered){
36813             var cs = this.childNodes;
36814             for(var i = 0, len = cs.length; i < len; i++){
36815                 cs[i].renderIndent(true, refresh);
36816             }
36817         }
36818     }
36819 });/*
36820  * Based on:
36821  * Ext JS Library 1.1.1
36822  * Copyright(c) 2006-2007, Ext JS, LLC.
36823  *
36824  * Originally Released Under LGPL - original licence link has changed is not relivant.
36825  *
36826  * Fork - LGPL
36827  * <script type="text/javascript">
36828  */
36829  
36830 /**
36831  * @class Roo.tree.AsyncTreeNode
36832  * @extends Roo.tree.TreeNode
36833  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36834  * @constructor
36835  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36836  */
36837  Roo.tree.AsyncTreeNode = function(config){
36838     this.loaded = false;
36839     this.loading = false;
36840     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36841     /**
36842     * @event beforeload
36843     * Fires before this node is loaded, return false to cancel
36844     * @param {Node} this This node
36845     */
36846     this.addEvents({'beforeload':true, 'load': true});
36847     /**
36848     * @event load
36849     * Fires when this node is loaded
36850     * @param {Node} this This node
36851     */
36852     /**
36853      * The loader used by this node (defaults to using the tree's defined loader)
36854      * @type TreeLoader
36855      * @property loader
36856      */
36857 };
36858 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36859     expand : function(deep, anim, callback){
36860         if(this.loading){ // if an async load is already running, waiting til it's done
36861             var timer;
36862             var f = function(){
36863                 if(!this.loading){ // done loading
36864                     clearInterval(timer);
36865                     this.expand(deep, anim, callback);
36866                 }
36867             }.createDelegate(this);
36868             timer = setInterval(f, 200);
36869             return;
36870         }
36871         if(!this.loaded){
36872             if(this.fireEvent("beforeload", this) === false){
36873                 return;
36874             }
36875             this.loading = true;
36876             this.ui.beforeLoad(this);
36877             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36878             if(loader){
36879                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36880                 return;
36881             }
36882         }
36883         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36884     },
36885     
36886     /**
36887      * Returns true if this node is currently loading
36888      * @return {Boolean}
36889      */
36890     isLoading : function(){
36891         return this.loading;  
36892     },
36893     
36894     loadComplete : function(deep, anim, callback){
36895         this.loading = false;
36896         this.loaded = true;
36897         this.ui.afterLoad(this);
36898         this.fireEvent("load", this);
36899         this.expand(deep, anim, callback);
36900     },
36901     
36902     /**
36903      * Returns true if this node has been loaded
36904      * @return {Boolean}
36905      */
36906     isLoaded : function(){
36907         return this.loaded;
36908     },
36909     
36910     hasChildNodes : function(){
36911         if(!this.isLeaf() && !this.loaded){
36912             return true;
36913         }else{
36914             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36915         }
36916     },
36917
36918     /**
36919      * Trigger a reload for this node
36920      * @param {Function} callback
36921      */
36922     reload : function(callback){
36923         this.collapse(false, false);
36924         while(this.firstChild){
36925             this.removeChild(this.firstChild);
36926         }
36927         this.childrenRendered = false;
36928         this.loaded = false;
36929         if(this.isHiddenRoot()){
36930             this.expanded = false;
36931         }
36932         this.expand(false, false, callback);
36933     }
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  * @class Roo.tree.TreeNodeUI
36947  * @constructor
36948  * @param {Object} node The node to render
36949  * The TreeNode UI implementation is separate from the
36950  * tree implementation. Unless you are customizing the tree UI,
36951  * you should never have to use this directly.
36952  */
36953 Roo.tree.TreeNodeUI = function(node){
36954     this.node = node;
36955     this.rendered = false;
36956     this.animating = false;
36957     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36958 };
36959
36960 Roo.tree.TreeNodeUI.prototype = {
36961     removeChild : function(node){
36962         if(this.rendered){
36963             this.ctNode.removeChild(node.ui.getEl());
36964         }
36965     },
36966
36967     beforeLoad : function(){
36968          this.addClass("x-tree-node-loading");
36969     },
36970
36971     afterLoad : function(){
36972          this.removeClass("x-tree-node-loading");
36973     },
36974
36975     onTextChange : function(node, text, oldText){
36976         if(this.rendered){
36977             this.textNode.innerHTML = text;
36978         }
36979     },
36980
36981     onDisableChange : function(node, state){
36982         this.disabled = state;
36983         if(state){
36984             this.addClass("x-tree-node-disabled");
36985         }else{
36986             this.removeClass("x-tree-node-disabled");
36987         }
36988     },
36989
36990     onSelectedChange : function(state){
36991         if(state){
36992             this.focus();
36993             this.addClass("x-tree-selected");
36994         }else{
36995             //this.blur();
36996             this.removeClass("x-tree-selected");
36997         }
36998     },
36999
37000     onMove : function(tree, node, oldParent, newParent, index, refNode){
37001         this.childIndent = null;
37002         if(this.rendered){
37003             var targetNode = newParent.ui.getContainer();
37004             if(!targetNode){//target not rendered
37005                 this.holder = document.createElement("div");
37006                 this.holder.appendChild(this.wrap);
37007                 return;
37008             }
37009             var insertBefore = refNode ? refNode.ui.getEl() : null;
37010             if(insertBefore){
37011                 targetNode.insertBefore(this.wrap, insertBefore);
37012             }else{
37013                 targetNode.appendChild(this.wrap);
37014             }
37015             this.node.renderIndent(true);
37016         }
37017     },
37018
37019     addClass : function(cls){
37020         if(this.elNode){
37021             Roo.fly(this.elNode).addClass(cls);
37022         }
37023     },
37024
37025     removeClass : function(cls){
37026         if(this.elNode){
37027             Roo.fly(this.elNode).removeClass(cls);
37028         }
37029     },
37030
37031     remove : function(){
37032         if(this.rendered){
37033             this.holder = document.createElement("div");
37034             this.holder.appendChild(this.wrap);
37035         }
37036     },
37037
37038     fireEvent : function(){
37039         return this.node.fireEvent.apply(this.node, arguments);
37040     },
37041
37042     initEvents : function(){
37043         this.node.on("move", this.onMove, this);
37044         var E = Roo.EventManager;
37045         var a = this.anchor;
37046
37047         var el = Roo.fly(a, '_treeui');
37048
37049         if(Roo.isOpera){ // opera render bug ignores the CSS
37050             el.setStyle("text-decoration", "none");
37051         }
37052
37053         el.on("click", this.onClick, this);
37054         el.on("dblclick", this.onDblClick, this);
37055
37056         if(this.checkbox){
37057             Roo.EventManager.on(this.checkbox,
37058                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37059         }
37060
37061         el.on("contextmenu", this.onContextMenu, this);
37062
37063         var icon = Roo.fly(this.iconNode);
37064         icon.on("click", this.onClick, this);
37065         icon.on("dblclick", this.onDblClick, this);
37066         icon.on("contextmenu", this.onContextMenu, this);
37067         E.on(this.ecNode, "click", this.ecClick, this, true);
37068
37069         if(this.node.disabled){
37070             this.addClass("x-tree-node-disabled");
37071         }
37072         if(this.node.hidden){
37073             this.addClass("x-tree-node-disabled");
37074         }
37075         var ot = this.node.getOwnerTree();
37076         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37077         if(dd && (!this.node.isRoot || ot.rootVisible)){
37078             Roo.dd.Registry.register(this.elNode, {
37079                 node: this.node,
37080                 handles: this.getDDHandles(),
37081                 isHandle: false
37082             });
37083         }
37084     },
37085
37086     getDDHandles : function(){
37087         return [this.iconNode, this.textNode];
37088     },
37089
37090     hide : function(){
37091         if(this.rendered){
37092             this.wrap.style.display = "none";
37093         }
37094     },
37095
37096     show : function(){
37097         if(this.rendered){
37098             this.wrap.style.display = "";
37099         }
37100     },
37101
37102     onContextMenu : function(e){
37103         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37104             e.preventDefault();
37105             this.focus();
37106             this.fireEvent("contextmenu", this.node, e);
37107         }
37108     },
37109
37110     onClick : function(e){
37111         if(this.dropping){
37112             e.stopEvent();
37113             return;
37114         }
37115         if(this.fireEvent("beforeclick", this.node, e) !== false){
37116             if(!this.disabled && this.node.attributes.href){
37117                 this.fireEvent("click", this.node, e);
37118                 return;
37119             }
37120             e.preventDefault();
37121             if(this.disabled){
37122                 return;
37123             }
37124
37125             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37126                 this.node.toggle();
37127             }
37128
37129             this.fireEvent("click", this.node, e);
37130         }else{
37131             e.stopEvent();
37132         }
37133     },
37134
37135     onDblClick : function(e){
37136         e.preventDefault();
37137         if(this.disabled){
37138             return;
37139         }
37140         if(this.checkbox){
37141             this.toggleCheck();
37142         }
37143         if(!this.animating && this.node.hasChildNodes()){
37144             this.node.toggle();
37145         }
37146         this.fireEvent("dblclick", this.node, e);
37147     },
37148
37149     onCheckChange : function(){
37150         var checked = this.checkbox.checked;
37151         this.node.attributes.checked = checked;
37152         this.fireEvent('checkchange', this.node, checked);
37153     },
37154
37155     ecClick : function(e){
37156         if(!this.animating && this.node.hasChildNodes()){
37157             this.node.toggle();
37158         }
37159     },
37160
37161     startDrop : function(){
37162         this.dropping = true;
37163     },
37164
37165     // delayed drop so the click event doesn't get fired on a drop
37166     endDrop : function(){
37167        setTimeout(function(){
37168            this.dropping = false;
37169        }.createDelegate(this), 50);
37170     },
37171
37172     expand : function(){
37173         this.updateExpandIcon();
37174         this.ctNode.style.display = "";
37175     },
37176
37177     focus : function(){
37178         if(!this.node.preventHScroll){
37179             try{this.anchor.focus();
37180             }catch(e){}
37181         }else if(!Roo.isIE){
37182             try{
37183                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37184                 var l = noscroll.scrollLeft;
37185                 this.anchor.focus();
37186                 noscroll.scrollLeft = l;
37187             }catch(e){}
37188         }
37189     },
37190
37191     toggleCheck : function(value){
37192         var cb = this.checkbox;
37193         if(cb){
37194             cb.checked = (value === undefined ? !cb.checked : value);
37195         }
37196     },
37197
37198     blur : function(){
37199         try{
37200             this.anchor.blur();
37201         }catch(e){}
37202     },
37203
37204     animExpand : function(callback){
37205         var ct = Roo.get(this.ctNode);
37206         ct.stopFx();
37207         if(!this.node.hasChildNodes()){
37208             this.updateExpandIcon();
37209             this.ctNode.style.display = "";
37210             Roo.callback(callback);
37211             return;
37212         }
37213         this.animating = true;
37214         this.updateExpandIcon();
37215
37216         ct.slideIn('t', {
37217            callback : function(){
37218                this.animating = false;
37219                Roo.callback(callback);
37220             },
37221             scope: this,
37222             duration: this.node.ownerTree.duration || .25
37223         });
37224     },
37225
37226     highlight : function(){
37227         var tree = this.node.getOwnerTree();
37228         Roo.fly(this.wrap).highlight(
37229             tree.hlColor || "C3DAF9",
37230             {endColor: tree.hlBaseColor}
37231         );
37232     },
37233
37234     collapse : function(){
37235         this.updateExpandIcon();
37236         this.ctNode.style.display = "none";
37237     },
37238
37239     animCollapse : function(callback){
37240         var ct = Roo.get(this.ctNode);
37241         ct.enableDisplayMode('block');
37242         ct.stopFx();
37243
37244         this.animating = true;
37245         this.updateExpandIcon();
37246
37247         ct.slideOut('t', {
37248             callback : function(){
37249                this.animating = false;
37250                Roo.callback(callback);
37251             },
37252             scope: this,
37253             duration: this.node.ownerTree.duration || .25
37254         });
37255     },
37256
37257     getContainer : function(){
37258         return this.ctNode;
37259     },
37260
37261     getEl : function(){
37262         return this.wrap;
37263     },
37264
37265     appendDDGhost : function(ghostNode){
37266         ghostNode.appendChild(this.elNode.cloneNode(true));
37267     },
37268
37269     getDDRepairXY : function(){
37270         return Roo.lib.Dom.getXY(this.iconNode);
37271     },
37272
37273     onRender : function(){
37274         this.render();
37275     },
37276
37277     render : function(bulkRender){
37278         var n = this.node, a = n.attributes;
37279         var targetNode = n.parentNode ?
37280               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37281
37282         if(!this.rendered){
37283             this.rendered = true;
37284
37285             this.renderElements(n, a, targetNode, bulkRender);
37286
37287             if(a.qtip){
37288                if(this.textNode.setAttributeNS){
37289                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37290                    if(a.qtipTitle){
37291                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37292                    }
37293                }else{
37294                    this.textNode.setAttribute("ext:qtip", a.qtip);
37295                    if(a.qtipTitle){
37296                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37297                    }
37298                }
37299             }else if(a.qtipCfg){
37300                 a.qtipCfg.target = Roo.id(this.textNode);
37301                 Roo.QuickTips.register(a.qtipCfg);
37302             }
37303             this.initEvents();
37304             if(!this.node.expanded){
37305                 this.updateExpandIcon();
37306             }
37307         }else{
37308             if(bulkRender === true) {
37309                 targetNode.appendChild(this.wrap);
37310             }
37311         }
37312     },
37313
37314     renderElements : function(n, a, targetNode, bulkRender)
37315     {
37316         // add some indent caching, this helps performance when rendering a large tree
37317         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37318         var t = n.getOwnerTree();
37319         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37320         if (typeof(n.attributes.html) != 'undefined') {
37321             txt = n.attributes.html;
37322         }
37323         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37324         var cb = typeof a.checked == 'boolean';
37325         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37326         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37327             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37328             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37329             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37330             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37331             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37332              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37333                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37334             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37335             "</li>"];
37336
37337         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37338             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37339                                 n.nextSibling.ui.getEl(), buf.join(""));
37340         }else{
37341             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37342         }
37343
37344         this.elNode = this.wrap.childNodes[0];
37345         this.ctNode = this.wrap.childNodes[1];
37346         var cs = this.elNode.childNodes;
37347         this.indentNode = cs[0];
37348         this.ecNode = cs[1];
37349         this.iconNode = cs[2];
37350         var index = 3;
37351         if(cb){
37352             this.checkbox = cs[3];
37353             index++;
37354         }
37355         this.anchor = cs[index];
37356         this.textNode = cs[index].firstChild;
37357     },
37358
37359     getAnchor : function(){
37360         return this.anchor;
37361     },
37362
37363     getTextEl : function(){
37364         return this.textNode;
37365     },
37366
37367     getIconEl : function(){
37368         return this.iconNode;
37369     },
37370
37371     isChecked : function(){
37372         return this.checkbox ? this.checkbox.checked : false;
37373     },
37374
37375     updateExpandIcon : function(){
37376         if(this.rendered){
37377             var n = this.node, c1, c2;
37378             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37379             var hasChild = n.hasChildNodes();
37380             if(hasChild){
37381                 if(n.expanded){
37382                     cls += "-minus";
37383                     c1 = "x-tree-node-collapsed";
37384                     c2 = "x-tree-node-expanded";
37385                 }else{
37386                     cls += "-plus";
37387                     c1 = "x-tree-node-expanded";
37388                     c2 = "x-tree-node-collapsed";
37389                 }
37390                 if(this.wasLeaf){
37391                     this.removeClass("x-tree-node-leaf");
37392                     this.wasLeaf = false;
37393                 }
37394                 if(this.c1 != c1 || this.c2 != c2){
37395                     Roo.fly(this.elNode).replaceClass(c1, c2);
37396                     this.c1 = c1; this.c2 = c2;
37397                 }
37398             }else{
37399                 // this changes non-leafs into leafs if they have no children.
37400                 // it's not very rational behaviour..
37401                 
37402                 if(!this.wasLeaf && this.node.leaf){
37403                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37404                     delete this.c1;
37405                     delete this.c2;
37406                     this.wasLeaf = true;
37407                 }
37408             }
37409             var ecc = "x-tree-ec-icon "+cls;
37410             if(this.ecc != ecc){
37411                 this.ecNode.className = ecc;
37412                 this.ecc = ecc;
37413             }
37414         }
37415     },
37416
37417     getChildIndent : function(){
37418         if(!this.childIndent){
37419             var buf = [];
37420             var p = this.node;
37421             while(p){
37422                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37423                     if(!p.isLast()) {
37424                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37425                     } else {
37426                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37427                     }
37428                 }
37429                 p = p.parentNode;
37430             }
37431             this.childIndent = buf.join("");
37432         }
37433         return this.childIndent;
37434     },
37435
37436     renderIndent : function(){
37437         if(this.rendered){
37438             var indent = "";
37439             var p = this.node.parentNode;
37440             if(p){
37441                 indent = p.ui.getChildIndent();
37442             }
37443             if(this.indentMarkup != indent){ // don't rerender if not required
37444                 this.indentNode.innerHTML = indent;
37445                 this.indentMarkup = indent;
37446             }
37447             this.updateExpandIcon();
37448         }
37449     }
37450 };
37451
37452 Roo.tree.RootTreeNodeUI = function(){
37453     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37454 };
37455 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37456     render : function(){
37457         if(!this.rendered){
37458             var targetNode = this.node.ownerTree.innerCt.dom;
37459             this.node.expanded = true;
37460             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37461             this.wrap = this.ctNode = targetNode.firstChild;
37462         }
37463     },
37464     collapse : function(){
37465     },
37466     expand : function(){
37467     }
37468 });/*
37469  * Based on:
37470  * Ext JS Library 1.1.1
37471  * Copyright(c) 2006-2007, Ext JS, LLC.
37472  *
37473  * Originally Released Under LGPL - original licence link has changed is not relivant.
37474  *
37475  * Fork - LGPL
37476  * <script type="text/javascript">
37477  */
37478 /**
37479  * @class Roo.tree.TreeLoader
37480  * @extends Roo.util.Observable
37481  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37482  * nodes from a specified URL. The response must be a javascript Array definition
37483  * who's elements are node definition objects. eg:
37484  * <pre><code>
37485 {  success : true,
37486    data :      [
37487    
37488     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37489     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37490     ]
37491 }
37492
37493
37494 </code></pre>
37495  * <br><br>
37496  * The old style respose with just an array is still supported, but not recommended.
37497  * <br><br>
37498  *
37499  * A server request is sent, and child nodes are loaded only when a node is expanded.
37500  * The loading node's id is passed to the server under the parameter name "node" to
37501  * enable the server to produce the correct child nodes.
37502  * <br><br>
37503  * To pass extra parameters, an event handler may be attached to the "beforeload"
37504  * event, and the parameters specified in the TreeLoader's baseParams property:
37505  * <pre><code>
37506     myTreeLoader.on("beforeload", function(treeLoader, node) {
37507         this.baseParams.category = node.attributes.category;
37508     }, this);
37509     
37510 </code></pre>
37511  *
37512  * This would pass an HTTP parameter called "category" to the server containing
37513  * the value of the Node's "category" attribute.
37514  * @constructor
37515  * Creates a new Treeloader.
37516  * @param {Object} config A config object containing config properties.
37517  */
37518 Roo.tree.TreeLoader = function(config){
37519     this.baseParams = {};
37520     this.requestMethod = "POST";
37521     Roo.apply(this, config);
37522
37523     this.addEvents({
37524     
37525         /**
37526          * @event beforeload
37527          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37528          * @param {Object} This TreeLoader object.
37529          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37530          * @param {Object} callback The callback function specified in the {@link #load} call.
37531          */
37532         beforeload : true,
37533         /**
37534          * @event load
37535          * Fires when the node has been successfuly loaded.
37536          * @param {Object} This TreeLoader object.
37537          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37538          * @param {Object} response The response object containing the data from the server.
37539          */
37540         load : true,
37541         /**
37542          * @event loadexception
37543          * Fires if the network request failed.
37544          * @param {Object} This TreeLoader object.
37545          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37546          * @param {Object} response The response object containing the data from the server.
37547          */
37548         loadexception : true,
37549         /**
37550          * @event create
37551          * Fires before a node is created, enabling you to return custom Node types 
37552          * @param {Object} This TreeLoader object.
37553          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37554          */
37555         create : true
37556     });
37557
37558     Roo.tree.TreeLoader.superclass.constructor.call(this);
37559 };
37560
37561 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37562     /**
37563     * @cfg {String} dataUrl The URL from which to request a Json string which
37564     * specifies an array of node definition object representing the child nodes
37565     * to be loaded.
37566     */
37567     /**
37568     * @cfg {String} requestMethod either GET or POST
37569     * defaults to POST (due to BC)
37570     * to be loaded.
37571     */
37572     /**
37573     * @cfg {Object} baseParams (optional) An object containing properties which
37574     * specify HTTP parameters to be passed to each request for child nodes.
37575     */
37576     /**
37577     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37578     * created by this loader. If the attributes sent by the server have an attribute in this object,
37579     * they take priority.
37580     */
37581     /**
37582     * @cfg {Object} uiProviders (optional) An object containing properties which
37583     * 
37584     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37585     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37586     * <i>uiProvider</i> attribute of a returned child node is a string rather
37587     * than a reference to a TreeNodeUI implementation, this that string value
37588     * is used as a property name in the uiProviders object. You can define the provider named
37589     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37590     */
37591     uiProviders : {},
37592
37593     /**
37594     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37595     * child nodes before loading.
37596     */
37597     clearOnLoad : true,
37598
37599     /**
37600     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37601     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37602     * Grid query { data : [ .....] }
37603     */
37604     
37605     root : false,
37606      /**
37607     * @cfg {String} queryParam (optional) 
37608     * Name of the query as it will be passed on the querystring (defaults to 'node')
37609     * eg. the request will be ?node=[id]
37610     */
37611     
37612     
37613     queryParam: false,
37614     
37615     /**
37616      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37617      * This is called automatically when a node is expanded, but may be used to reload
37618      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37619      * @param {Roo.tree.TreeNode} node
37620      * @param {Function} callback
37621      */
37622     load : function(node, callback){
37623         if(this.clearOnLoad){
37624             while(node.firstChild){
37625                 node.removeChild(node.firstChild);
37626             }
37627         }
37628         if(node.attributes.children){ // preloaded json children
37629             var cs = node.attributes.children;
37630             for(var i = 0, len = cs.length; i < len; i++){
37631                 node.appendChild(this.createNode(cs[i]));
37632             }
37633             if(typeof callback == "function"){
37634                 callback();
37635             }
37636         }else if(this.dataUrl){
37637             this.requestData(node, callback);
37638         }
37639     },
37640
37641     getParams: function(node){
37642         var buf = [], bp = this.baseParams;
37643         for(var key in bp){
37644             if(typeof bp[key] != "function"){
37645                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37646             }
37647         }
37648         var n = this.queryParam === false ? 'node' : this.queryParam;
37649         buf.push(n + "=", encodeURIComponent(node.id));
37650         return buf.join("");
37651     },
37652
37653     requestData : function(node, callback){
37654         if(this.fireEvent("beforeload", this, node, callback) !== false){
37655             this.transId = Roo.Ajax.request({
37656                 method:this.requestMethod,
37657                 url: this.dataUrl||this.url,
37658                 success: this.handleResponse,
37659                 failure: this.handleFailure,
37660                 scope: this,
37661                 argument: {callback: callback, node: node},
37662                 params: this.getParams(node)
37663             });
37664         }else{
37665             // if the load is cancelled, make sure we notify
37666             // the node that we are done
37667             if(typeof callback == "function"){
37668                 callback();
37669             }
37670         }
37671     },
37672
37673     isLoading : function(){
37674         return this.transId ? true : false;
37675     },
37676
37677     abort : function(){
37678         if(this.isLoading()){
37679             Roo.Ajax.abort(this.transId);
37680         }
37681     },
37682
37683     // private
37684     createNode : function(attr)
37685     {
37686         // apply baseAttrs, nice idea Corey!
37687         if(this.baseAttrs){
37688             Roo.applyIf(attr, this.baseAttrs);
37689         }
37690         if(this.applyLoader !== false){
37691             attr.loader = this;
37692         }
37693         // uiProvider = depreciated..
37694         
37695         if(typeof(attr.uiProvider) == 'string'){
37696            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37697                 /**  eval:var:attr */ eval(attr.uiProvider);
37698         }
37699         if(typeof(this.uiProviders['default']) != 'undefined') {
37700             attr.uiProvider = this.uiProviders['default'];
37701         }
37702         
37703         this.fireEvent('create', this, attr);
37704         
37705         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37706         return(attr.leaf ?
37707                         new Roo.tree.TreeNode(attr) :
37708                         new Roo.tree.AsyncTreeNode(attr));
37709     },
37710
37711     processResponse : function(response, node, callback)
37712     {
37713         var json = response.responseText;
37714         try {
37715             
37716             var o = Roo.decode(json);
37717             
37718             if (this.root === false && typeof(o.success) != undefined) {
37719                 this.root = 'data'; // the default behaviour for list like data..
37720                 }
37721                 
37722             if (this.root !== false &&  !o.success) {
37723                 // it's a failure condition.
37724                 var a = response.argument;
37725                 this.fireEvent("loadexception", this, a.node, response);
37726                 Roo.log("Load failed - should have a handler really");
37727                 return;
37728             }
37729             
37730             
37731             
37732             if (this.root !== false) {
37733                  o = o[this.root];
37734             }
37735             
37736             for(var i = 0, len = o.length; i < len; i++){
37737                 var n = this.createNode(o[i]);
37738                 if(n){
37739                     node.appendChild(n);
37740                 }
37741             }
37742             if(typeof callback == "function"){
37743                 callback(this, node);
37744             }
37745         }catch(e){
37746             this.handleFailure(response);
37747         }
37748     },
37749
37750     handleResponse : function(response){
37751         this.transId = false;
37752         var a = response.argument;
37753         this.processResponse(response, a.node, a.callback);
37754         this.fireEvent("load", this, a.node, response);
37755     },
37756
37757     handleFailure : function(response)
37758     {
37759         // should handle failure better..
37760         this.transId = false;
37761         var a = response.argument;
37762         this.fireEvent("loadexception", this, a.node, response);
37763         if(typeof a.callback == "function"){
37764             a.callback(this, a.node);
37765         }
37766     }
37767 });/*
37768  * Based on:
37769  * Ext JS Library 1.1.1
37770  * Copyright(c) 2006-2007, Ext JS, LLC.
37771  *
37772  * Originally Released Under LGPL - original licence link has changed is not relivant.
37773  *
37774  * Fork - LGPL
37775  * <script type="text/javascript">
37776  */
37777
37778 /**
37779 * @class Roo.tree.TreeFilter
37780 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37781 * @param {TreePanel} tree
37782 * @param {Object} config (optional)
37783  */
37784 Roo.tree.TreeFilter = function(tree, config){
37785     this.tree = tree;
37786     this.filtered = {};
37787     Roo.apply(this, config);
37788 };
37789
37790 Roo.tree.TreeFilter.prototype = {
37791     clearBlank:false,
37792     reverse:false,
37793     autoClear:false,
37794     remove:false,
37795
37796      /**
37797      * Filter the data by a specific attribute.
37798      * @param {String/RegExp} value Either string that the attribute value
37799      * should start with or a RegExp to test against the attribute
37800      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37801      * @param {TreeNode} startNode (optional) The node to start the filter at.
37802      */
37803     filter : function(value, attr, startNode){
37804         attr = attr || "text";
37805         var f;
37806         if(typeof value == "string"){
37807             var vlen = value.length;
37808             // auto clear empty filter
37809             if(vlen == 0 && this.clearBlank){
37810                 this.clear();
37811                 return;
37812             }
37813             value = value.toLowerCase();
37814             f = function(n){
37815                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37816             };
37817         }else if(value.exec){ // regex?
37818             f = function(n){
37819                 return value.test(n.attributes[attr]);
37820             };
37821         }else{
37822             throw 'Illegal filter type, must be string or regex';
37823         }
37824         this.filterBy(f, null, startNode);
37825         },
37826
37827     /**
37828      * Filter by a function. The passed function will be called with each
37829      * node in the tree (or from the startNode). If the function returns true, the node is kept
37830      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37831      * @param {Function} fn The filter function
37832      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37833      */
37834     filterBy : function(fn, scope, startNode){
37835         startNode = startNode || this.tree.root;
37836         if(this.autoClear){
37837             this.clear();
37838         }
37839         var af = this.filtered, rv = this.reverse;
37840         var f = function(n){
37841             if(n == startNode){
37842                 return true;
37843             }
37844             if(af[n.id]){
37845                 return false;
37846             }
37847             var m = fn.call(scope || n, n);
37848             if(!m || rv){
37849                 af[n.id] = n;
37850                 n.ui.hide();
37851                 return false;
37852             }
37853             return true;
37854         };
37855         startNode.cascade(f);
37856         if(this.remove){
37857            for(var id in af){
37858                if(typeof id != "function"){
37859                    var n = af[id];
37860                    if(n && n.parentNode){
37861                        n.parentNode.removeChild(n);
37862                    }
37863                }
37864            }
37865         }
37866     },
37867
37868     /**
37869      * Clears the current filter. Note: with the "remove" option
37870      * set a filter cannot be cleared.
37871      */
37872     clear : function(){
37873         var t = this.tree;
37874         var af = this.filtered;
37875         for(var id in af){
37876             if(typeof id != "function"){
37877                 var n = af[id];
37878                 if(n){
37879                     n.ui.show();
37880                 }
37881             }
37882         }
37883         this.filtered = {};
37884     }
37885 };
37886 /*
37887  * Based on:
37888  * Ext JS Library 1.1.1
37889  * Copyright(c) 2006-2007, Ext JS, LLC.
37890  *
37891  * Originally Released Under LGPL - original licence link has changed is not relivant.
37892  *
37893  * Fork - LGPL
37894  * <script type="text/javascript">
37895  */
37896  
37897
37898 /**
37899  * @class Roo.tree.TreeSorter
37900  * Provides sorting of nodes in a TreePanel
37901  * 
37902  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37903  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37904  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37905  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37906  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37907  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37908  * @constructor
37909  * @param {TreePanel} tree
37910  * @param {Object} config
37911  */
37912 Roo.tree.TreeSorter = function(tree, config){
37913     Roo.apply(this, config);
37914     tree.on("beforechildrenrendered", this.doSort, this);
37915     tree.on("append", this.updateSort, this);
37916     tree.on("insert", this.updateSort, this);
37917     
37918     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37919     var p = this.property || "text";
37920     var sortType = this.sortType;
37921     var fs = this.folderSort;
37922     var cs = this.caseSensitive === true;
37923     var leafAttr = this.leafAttr || 'leaf';
37924
37925     this.sortFn = function(n1, n2){
37926         if(fs){
37927             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37928                 return 1;
37929             }
37930             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37931                 return -1;
37932             }
37933         }
37934         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37935         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37936         if(v1 < v2){
37937                         return dsc ? +1 : -1;
37938                 }else if(v1 > v2){
37939                         return dsc ? -1 : +1;
37940         }else{
37941                 return 0;
37942         }
37943     };
37944 };
37945
37946 Roo.tree.TreeSorter.prototype = {
37947     doSort : function(node){
37948         node.sort(this.sortFn);
37949     },
37950     
37951     compareNodes : function(n1, n2){
37952         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37953     },
37954     
37955     updateSort : function(tree, node){
37956         if(node.childrenRendered){
37957             this.doSort.defer(1, this, [node]);
37958         }
37959     }
37960 };/*
37961  * Based on:
37962  * Ext JS Library 1.1.1
37963  * Copyright(c) 2006-2007, Ext JS, LLC.
37964  *
37965  * Originally Released Under LGPL - original licence link has changed is not relivant.
37966  *
37967  * Fork - LGPL
37968  * <script type="text/javascript">
37969  */
37970
37971 if(Roo.dd.DropZone){
37972     
37973 Roo.tree.TreeDropZone = function(tree, config){
37974     this.allowParentInsert = false;
37975     this.allowContainerDrop = false;
37976     this.appendOnly = false;
37977     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37978     this.tree = tree;
37979     this.lastInsertClass = "x-tree-no-status";
37980     this.dragOverData = {};
37981 };
37982
37983 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37984     ddGroup : "TreeDD",
37985     scroll:  true,
37986     
37987     expandDelay : 1000,
37988     
37989     expandNode : function(node){
37990         if(node.hasChildNodes() && !node.isExpanded()){
37991             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37992         }
37993     },
37994     
37995     queueExpand : function(node){
37996         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37997     },
37998     
37999     cancelExpand : function(){
38000         if(this.expandProcId){
38001             clearTimeout(this.expandProcId);
38002             this.expandProcId = false;
38003         }
38004     },
38005     
38006     isValidDropPoint : function(n, pt, dd, e, data){
38007         if(!n || !data){ return false; }
38008         var targetNode = n.node;
38009         var dropNode = data.node;
38010         // default drop rules
38011         if(!(targetNode && targetNode.isTarget && pt)){
38012             return false;
38013         }
38014         if(pt == "append" && targetNode.allowChildren === false){
38015             return false;
38016         }
38017         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38018             return false;
38019         }
38020         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38021             return false;
38022         }
38023         // reuse the object
38024         var overEvent = this.dragOverData;
38025         overEvent.tree = this.tree;
38026         overEvent.target = targetNode;
38027         overEvent.data = data;
38028         overEvent.point = pt;
38029         overEvent.source = dd;
38030         overEvent.rawEvent = e;
38031         overEvent.dropNode = dropNode;
38032         overEvent.cancel = false;  
38033         var result = this.tree.fireEvent("nodedragover", overEvent);
38034         return overEvent.cancel === false && result !== false;
38035     },
38036     
38037     getDropPoint : function(e, n, dd)
38038     {
38039         var tn = n.node;
38040         if(tn.isRoot){
38041             return tn.allowChildren !== false ? "append" : false; // always append for root
38042         }
38043         var dragEl = n.ddel;
38044         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38045         var y = Roo.lib.Event.getPageY(e);
38046         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38047         
38048         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38049         var noAppend = tn.allowChildren === false;
38050         if(this.appendOnly || tn.parentNode.allowChildren === false){
38051             return noAppend ? false : "append";
38052         }
38053         var noBelow = false;
38054         if(!this.allowParentInsert){
38055             noBelow = tn.hasChildNodes() && tn.isExpanded();
38056         }
38057         var q = (b - t) / (noAppend ? 2 : 3);
38058         if(y >= t && y < (t + q)){
38059             return "above";
38060         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38061             return "below";
38062         }else{
38063             return "append";
38064         }
38065     },
38066     
38067     onNodeEnter : function(n, dd, e, data)
38068     {
38069         this.cancelExpand();
38070     },
38071     
38072     onNodeOver : function(n, dd, e, data)
38073     {
38074        
38075         var pt = this.getDropPoint(e, n, dd);
38076         var node = n.node;
38077         
38078         // auto node expand check
38079         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38080             this.queueExpand(node);
38081         }else if(pt != "append"){
38082             this.cancelExpand();
38083         }
38084         
38085         // set the insert point style on the target node
38086         var returnCls = this.dropNotAllowed;
38087         if(this.isValidDropPoint(n, pt, dd, e, data)){
38088            if(pt){
38089                var el = n.ddel;
38090                var cls;
38091                if(pt == "above"){
38092                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38093                    cls = "x-tree-drag-insert-above";
38094                }else if(pt == "below"){
38095                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38096                    cls = "x-tree-drag-insert-below";
38097                }else{
38098                    returnCls = "x-tree-drop-ok-append";
38099                    cls = "x-tree-drag-append";
38100                }
38101                if(this.lastInsertClass != cls){
38102                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38103                    this.lastInsertClass = cls;
38104                }
38105            }
38106        }
38107        return returnCls;
38108     },
38109     
38110     onNodeOut : function(n, dd, e, data){
38111         
38112         this.cancelExpand();
38113         this.removeDropIndicators(n);
38114     },
38115     
38116     onNodeDrop : function(n, dd, e, data){
38117         var point = this.getDropPoint(e, n, dd);
38118         var targetNode = n.node;
38119         targetNode.ui.startDrop();
38120         if(!this.isValidDropPoint(n, point, dd, e, data)){
38121             targetNode.ui.endDrop();
38122             return false;
38123         }
38124         // first try to find the drop node
38125         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38126         var dropEvent = {
38127             tree : this.tree,
38128             target: targetNode,
38129             data: data,
38130             point: point,
38131             source: dd,
38132             rawEvent: e,
38133             dropNode: dropNode,
38134             cancel: !dropNode   
38135         };
38136         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38137         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38138             targetNode.ui.endDrop();
38139             return false;
38140         }
38141         // allow target changing
38142         targetNode = dropEvent.target;
38143         if(point == "append" && !targetNode.isExpanded()){
38144             targetNode.expand(false, null, function(){
38145                 this.completeDrop(dropEvent);
38146             }.createDelegate(this));
38147         }else{
38148             this.completeDrop(dropEvent);
38149         }
38150         return true;
38151     },
38152     
38153     completeDrop : function(de){
38154         var ns = de.dropNode, p = de.point, t = de.target;
38155         if(!(ns instanceof Array)){
38156             ns = [ns];
38157         }
38158         var n;
38159         for(var i = 0, len = ns.length; i < len; i++){
38160             n = ns[i];
38161             if(p == "above"){
38162                 t.parentNode.insertBefore(n, t);
38163             }else if(p == "below"){
38164                 t.parentNode.insertBefore(n, t.nextSibling);
38165             }else{
38166                 t.appendChild(n);
38167             }
38168         }
38169         n.ui.focus();
38170         if(this.tree.hlDrop){
38171             n.ui.highlight();
38172         }
38173         t.ui.endDrop();
38174         this.tree.fireEvent("nodedrop", de);
38175     },
38176     
38177     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38178         if(this.tree.hlDrop){
38179             dropNode.ui.focus();
38180             dropNode.ui.highlight();
38181         }
38182         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38183     },
38184     
38185     getTree : function(){
38186         return this.tree;
38187     },
38188     
38189     removeDropIndicators : function(n){
38190         if(n && n.ddel){
38191             var el = n.ddel;
38192             Roo.fly(el).removeClass([
38193                     "x-tree-drag-insert-above",
38194                     "x-tree-drag-insert-below",
38195                     "x-tree-drag-append"]);
38196             this.lastInsertClass = "_noclass";
38197         }
38198     },
38199     
38200     beforeDragDrop : function(target, e, id){
38201         this.cancelExpand();
38202         return true;
38203     },
38204     
38205     afterRepair : function(data){
38206         if(data && Roo.enableFx){
38207             data.node.ui.highlight();
38208         }
38209         this.hideProxy();
38210     } 
38211     
38212 });
38213
38214 }
38215 /*
38216  * Based on:
38217  * Ext JS Library 1.1.1
38218  * Copyright(c) 2006-2007, Ext JS, LLC.
38219  *
38220  * Originally Released Under LGPL - original licence link has changed is not relivant.
38221  *
38222  * Fork - LGPL
38223  * <script type="text/javascript">
38224  */
38225  
38226
38227 if(Roo.dd.DragZone){
38228 Roo.tree.TreeDragZone = function(tree, config){
38229     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38230     this.tree = tree;
38231 };
38232
38233 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38234     ddGroup : "TreeDD",
38235    
38236     onBeforeDrag : function(data, e){
38237         var n = data.node;
38238         return n && n.draggable && !n.disabled;
38239     },
38240      
38241     
38242     onInitDrag : function(e){
38243         var data = this.dragData;
38244         this.tree.getSelectionModel().select(data.node);
38245         this.proxy.update("");
38246         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38247         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38248     },
38249     
38250     getRepairXY : function(e, data){
38251         return data.node.ui.getDDRepairXY();
38252     },
38253     
38254     onEndDrag : function(data, e){
38255         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38256         
38257         
38258     },
38259     
38260     onValidDrop : function(dd, e, id){
38261         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38262         this.hideProxy();
38263     },
38264     
38265     beforeInvalidDrop : function(e, id){
38266         // this scrolls the original position back into view
38267         var sm = this.tree.getSelectionModel();
38268         sm.clearSelections();
38269         sm.select(this.dragData.node);
38270     }
38271 });
38272 }/*
38273  * Based on:
38274  * Ext JS Library 1.1.1
38275  * Copyright(c) 2006-2007, Ext JS, LLC.
38276  *
38277  * Originally Released Under LGPL - original licence link has changed is not relivant.
38278  *
38279  * Fork - LGPL
38280  * <script type="text/javascript">
38281  */
38282 /**
38283  * @class Roo.tree.TreeEditor
38284  * @extends Roo.Editor
38285  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38286  * as the editor field.
38287  * @constructor
38288  * @param {Object} config (used to be the tree panel.)
38289  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38290  * 
38291  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38292  * @cfg {Roo.form.TextField} field [required] The field configuration
38293  *
38294  * 
38295  */
38296 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38297     var tree = config;
38298     var field;
38299     if (oldconfig) { // old style..
38300         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38301     } else {
38302         // new style..
38303         tree = config.tree;
38304         config.field = config.field  || {};
38305         config.field.xtype = 'TextField';
38306         field = Roo.factory(config.field, Roo.form);
38307     }
38308     config = config || {};
38309     
38310     
38311     this.addEvents({
38312         /**
38313          * @event beforenodeedit
38314          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38315          * false from the handler of this event.
38316          * @param {Editor} this
38317          * @param {Roo.tree.Node} node 
38318          */
38319         "beforenodeedit" : true
38320     });
38321     
38322     //Roo.log(config);
38323     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38324
38325     this.tree = tree;
38326
38327     tree.on('beforeclick', this.beforeNodeClick, this);
38328     tree.getTreeEl().on('mousedown', this.hide, this);
38329     this.on('complete', this.updateNode, this);
38330     this.on('beforestartedit', this.fitToTree, this);
38331     this.on('startedit', this.bindScroll, this, {delay:10});
38332     this.on('specialkey', this.onSpecialKey, this);
38333 };
38334
38335 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38336     /**
38337      * @cfg {String} alignment
38338      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38339      */
38340     alignment: "l-l",
38341     // inherit
38342     autoSize: false,
38343     /**
38344      * @cfg {Boolean} hideEl
38345      * True to hide the bound element while the editor is displayed (defaults to false)
38346      */
38347     hideEl : false,
38348     /**
38349      * @cfg {String} cls
38350      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38351      */
38352     cls: "x-small-editor x-tree-editor",
38353     /**
38354      * @cfg {Boolean} shim
38355      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38356      */
38357     shim:false,
38358     // inherit
38359     shadow:"frame",
38360     /**
38361      * @cfg {Number} maxWidth
38362      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38363      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38364      * scroll and client offsets into account prior to each edit.
38365      */
38366     maxWidth: 250,
38367
38368     editDelay : 350,
38369
38370     // private
38371     fitToTree : function(ed, el){
38372         var td = this.tree.getTreeEl().dom, nd = el.dom;
38373         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38374             td.scrollLeft = nd.offsetLeft;
38375         }
38376         var w = Math.min(
38377                 this.maxWidth,
38378                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38379         this.setSize(w, '');
38380         
38381         return this.fireEvent('beforenodeedit', this, this.editNode);
38382         
38383     },
38384
38385     // private
38386     triggerEdit : function(node){
38387         this.completeEdit();
38388         this.editNode = node;
38389         this.startEdit(node.ui.textNode, node.text);
38390     },
38391
38392     // private
38393     bindScroll : function(){
38394         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38395     },
38396
38397     // private
38398     beforeNodeClick : function(node, e){
38399         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38400         this.lastClick = new Date();
38401         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38402             e.stopEvent();
38403             this.triggerEdit(node);
38404             return false;
38405         }
38406         return true;
38407     },
38408
38409     // private
38410     updateNode : function(ed, value){
38411         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38412         this.editNode.setText(value);
38413     },
38414
38415     // private
38416     onHide : function(){
38417         Roo.tree.TreeEditor.superclass.onHide.call(this);
38418         if(this.editNode){
38419             this.editNode.ui.focus();
38420         }
38421     },
38422
38423     // private
38424     onSpecialKey : function(field, e){
38425         var k = e.getKey();
38426         if(k == e.ESC){
38427             e.stopEvent();
38428             this.cancelEdit();
38429         }else if(k == e.ENTER && !e.hasModifier()){
38430             e.stopEvent();
38431             this.completeEdit();
38432         }
38433     }
38434 });//<Script type="text/javascript">
38435 /*
38436  * Based on:
38437  * Ext JS Library 1.1.1
38438  * Copyright(c) 2006-2007, Ext JS, LLC.
38439  *
38440  * Originally Released Under LGPL - original licence link has changed is not relivant.
38441  *
38442  * Fork - LGPL
38443  * <script type="text/javascript">
38444  */
38445  
38446 /**
38447  * Not documented??? - probably should be...
38448  */
38449
38450 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38451     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38452     
38453     renderElements : function(n, a, targetNode, bulkRender){
38454         //consel.log("renderElements?");
38455         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38456
38457         var t = n.getOwnerTree();
38458         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38459         
38460         var cols = t.columns;
38461         var bw = t.borderWidth;
38462         var c = cols[0];
38463         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38464          var cb = typeof a.checked == "boolean";
38465         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38466         var colcls = 'x-t-' + tid + '-c0';
38467         var buf = [
38468             '<li class="x-tree-node">',
38469             
38470                 
38471                 '<div class="x-tree-node-el ', a.cls,'">',
38472                     // extran...
38473                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38474                 
38475                 
38476                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38477                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38478                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38479                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38480                            (a.iconCls ? ' '+a.iconCls : ''),
38481                            '" unselectable="on" />',
38482                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38483                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38484                              
38485                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38486                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38487                             '<span unselectable="on" qtip="' + tx + '">',
38488                              tx,
38489                              '</span></a>' ,
38490                     '</div>',
38491                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38492                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38493                  ];
38494         for(var i = 1, len = cols.length; i < len; i++){
38495             c = cols[i];
38496             colcls = 'x-t-' + tid + '-c' +i;
38497             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38498             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38499                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38500                       "</div>");
38501          }
38502          
38503          buf.push(
38504             '</a>',
38505             '<div class="x-clear"></div></div>',
38506             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38507             "</li>");
38508         
38509         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38510             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38511                                 n.nextSibling.ui.getEl(), buf.join(""));
38512         }else{
38513             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38514         }
38515         var el = this.wrap.firstChild;
38516         this.elRow = el;
38517         this.elNode = el.firstChild;
38518         this.ranchor = el.childNodes[1];
38519         this.ctNode = this.wrap.childNodes[1];
38520         var cs = el.firstChild.childNodes;
38521         this.indentNode = cs[0];
38522         this.ecNode = cs[1];
38523         this.iconNode = cs[2];
38524         var index = 3;
38525         if(cb){
38526             this.checkbox = cs[3];
38527             index++;
38528         }
38529         this.anchor = cs[index];
38530         
38531         this.textNode = cs[index].firstChild;
38532         
38533         //el.on("click", this.onClick, this);
38534         //el.on("dblclick", this.onDblClick, this);
38535         
38536         
38537        // console.log(this);
38538     },
38539     initEvents : function(){
38540         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38541         
38542             
38543         var a = this.ranchor;
38544
38545         var el = Roo.get(a);
38546
38547         if(Roo.isOpera){ // opera render bug ignores the CSS
38548             el.setStyle("text-decoration", "none");
38549         }
38550
38551         el.on("click", this.onClick, this);
38552         el.on("dblclick", this.onDblClick, this);
38553         el.on("contextmenu", this.onContextMenu, this);
38554         
38555     },
38556     
38557     /*onSelectedChange : function(state){
38558         if(state){
38559             this.focus();
38560             this.addClass("x-tree-selected");
38561         }else{
38562             //this.blur();
38563             this.removeClass("x-tree-selected");
38564         }
38565     },*/
38566     addClass : function(cls){
38567         if(this.elRow){
38568             Roo.fly(this.elRow).addClass(cls);
38569         }
38570         
38571     },
38572     
38573     
38574     removeClass : function(cls){
38575         if(this.elRow){
38576             Roo.fly(this.elRow).removeClass(cls);
38577         }
38578     }
38579
38580     
38581     
38582 });//<Script type="text/javascript">
38583
38584 /*
38585  * Based on:
38586  * Ext JS Library 1.1.1
38587  * Copyright(c) 2006-2007, Ext JS, LLC.
38588  *
38589  * Originally Released Under LGPL - original licence link has changed is not relivant.
38590  *
38591  * Fork - LGPL
38592  * <script type="text/javascript">
38593  */
38594  
38595
38596 /**
38597  * @class Roo.tree.ColumnTree
38598  * @extends Roo.tree.TreePanel
38599  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38600  * @cfg {int} borderWidth  compined right/left border allowance
38601  * @constructor
38602  * @param {String/HTMLElement/Element} el The container element
38603  * @param {Object} config
38604  */
38605 Roo.tree.ColumnTree =  function(el, config)
38606 {
38607    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38608    this.addEvents({
38609         /**
38610         * @event resize
38611         * Fire this event on a container when it resizes
38612         * @param {int} w Width
38613         * @param {int} h Height
38614         */
38615        "resize" : true
38616     });
38617     this.on('resize', this.onResize, this);
38618 };
38619
38620 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38621     //lines:false,
38622     
38623     
38624     borderWidth: Roo.isBorderBox ? 0 : 2, 
38625     headEls : false,
38626     
38627     render : function(){
38628         // add the header.....
38629        
38630         Roo.tree.ColumnTree.superclass.render.apply(this);
38631         
38632         this.el.addClass('x-column-tree');
38633         
38634         this.headers = this.el.createChild(
38635             {cls:'x-tree-headers'},this.innerCt.dom);
38636    
38637         var cols = this.columns, c;
38638         var totalWidth = 0;
38639         this.headEls = [];
38640         var  len = cols.length;
38641         for(var i = 0; i < len; i++){
38642              c = cols[i];
38643              totalWidth += c.width;
38644             this.headEls.push(this.headers.createChild({
38645                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38646                  cn: {
38647                      cls:'x-tree-hd-text',
38648                      html: c.header
38649                  },
38650                  style:'width:'+(c.width-this.borderWidth)+'px;'
38651              }));
38652         }
38653         this.headers.createChild({cls:'x-clear'});
38654         // prevent floats from wrapping when clipped
38655         this.headers.setWidth(totalWidth);
38656         //this.innerCt.setWidth(totalWidth);
38657         this.innerCt.setStyle({ overflow: 'auto' });
38658         this.onResize(this.width, this.height);
38659              
38660         
38661     },
38662     onResize : function(w,h)
38663     {
38664         this.height = h;
38665         this.width = w;
38666         // resize cols..
38667         this.innerCt.setWidth(this.width);
38668         this.innerCt.setHeight(this.height-20);
38669         
38670         // headers...
38671         var cols = this.columns, c;
38672         var totalWidth = 0;
38673         var expEl = false;
38674         var len = cols.length;
38675         for(var i = 0; i < len; i++){
38676             c = cols[i];
38677             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38678                 // it's the expander..
38679                 expEl  = this.headEls[i];
38680                 continue;
38681             }
38682             totalWidth += c.width;
38683             
38684         }
38685         if (expEl) {
38686             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38687         }
38688         this.headers.setWidth(w-20);
38689
38690         
38691         
38692         
38693     }
38694 });
38695 /*
38696  * Based on:
38697  * Ext JS Library 1.1.1
38698  * Copyright(c) 2006-2007, Ext JS, LLC.
38699  *
38700  * Originally Released Under LGPL - original licence link has changed is not relivant.
38701  *
38702  * Fork - LGPL
38703  * <script type="text/javascript">
38704  */
38705  
38706 /**
38707  * @class Roo.menu.Menu
38708  * @extends Roo.util.Observable
38709  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38710  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38711  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38712  * @constructor
38713  * Creates a new Menu
38714  * @param {Object} config Configuration options
38715  */
38716 Roo.menu.Menu = function(config){
38717     
38718     Roo.menu.Menu.superclass.constructor.call(this, config);
38719     
38720     this.id = this.id || Roo.id();
38721     this.addEvents({
38722         /**
38723          * @event beforeshow
38724          * Fires before this menu is displayed
38725          * @param {Roo.menu.Menu} this
38726          */
38727         beforeshow : true,
38728         /**
38729          * @event beforehide
38730          * Fires before this menu is hidden
38731          * @param {Roo.menu.Menu} this
38732          */
38733         beforehide : true,
38734         /**
38735          * @event show
38736          * Fires after this menu is displayed
38737          * @param {Roo.menu.Menu} this
38738          */
38739         show : true,
38740         /**
38741          * @event hide
38742          * Fires after this menu is hidden
38743          * @param {Roo.menu.Menu} this
38744          */
38745         hide : true,
38746         /**
38747          * @event click
38748          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38749          * @param {Roo.menu.Menu} this
38750          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38751          * @param {Roo.EventObject} e
38752          */
38753         click : true,
38754         /**
38755          * @event mouseover
38756          * Fires when the mouse is hovering over this menu
38757          * @param {Roo.menu.Menu} this
38758          * @param {Roo.EventObject} e
38759          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38760          */
38761         mouseover : true,
38762         /**
38763          * @event mouseout
38764          * Fires when the mouse exits this menu
38765          * @param {Roo.menu.Menu} this
38766          * @param {Roo.EventObject} e
38767          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38768          */
38769         mouseout : true,
38770         /**
38771          * @event itemclick
38772          * Fires when a menu item contained in this menu is clicked
38773          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38774          * @param {Roo.EventObject} e
38775          */
38776         itemclick: true
38777     });
38778     if (this.registerMenu) {
38779         Roo.menu.MenuMgr.register(this);
38780     }
38781     
38782     var mis = this.items;
38783     this.items = new Roo.util.MixedCollection();
38784     if(mis){
38785         this.add.apply(this, mis);
38786     }
38787 };
38788
38789 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38790     /**
38791      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38792      */
38793     minWidth : 120,
38794     /**
38795      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38796      * for bottom-right shadow (defaults to "sides")
38797      */
38798     shadow : "sides",
38799     /**
38800      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38801      * this menu (defaults to "tl-tr?")
38802      */
38803     subMenuAlign : "tl-tr?",
38804     /**
38805      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38806      * relative to its element of origin (defaults to "tl-bl?")
38807      */
38808     defaultAlign : "tl-bl?",
38809     /**
38810      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38811      */
38812     allowOtherMenus : false,
38813     /**
38814      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38815      */
38816     registerMenu : true,
38817
38818     hidden:true,
38819
38820     // private
38821     render : function(){
38822         if(this.el){
38823             return;
38824         }
38825         var el = this.el = new Roo.Layer({
38826             cls: "x-menu",
38827             shadow:this.shadow,
38828             constrain: false,
38829             parentEl: this.parentEl || document.body,
38830             zindex:15000
38831         });
38832
38833         this.keyNav = new Roo.menu.MenuNav(this);
38834
38835         if(this.plain){
38836             el.addClass("x-menu-plain");
38837         }
38838         if(this.cls){
38839             el.addClass(this.cls);
38840         }
38841         // generic focus element
38842         this.focusEl = el.createChild({
38843             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38844         });
38845         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38846         //disabling touch- as it's causing issues ..
38847         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38848         ul.on('click'   , this.onClick, this);
38849         
38850         
38851         ul.on("mouseover", this.onMouseOver, this);
38852         ul.on("mouseout", this.onMouseOut, this);
38853         this.items.each(function(item){
38854             if (item.hidden) {
38855                 return;
38856             }
38857             
38858             var li = document.createElement("li");
38859             li.className = "x-menu-list-item";
38860             ul.dom.appendChild(li);
38861             item.render(li, this);
38862         }, this);
38863         this.ul = ul;
38864         this.autoWidth();
38865     },
38866
38867     // private
38868     autoWidth : function(){
38869         var el = this.el, ul = this.ul;
38870         if(!el){
38871             return;
38872         }
38873         var w = this.width;
38874         if(w){
38875             el.setWidth(w);
38876         }else if(Roo.isIE){
38877             el.setWidth(this.minWidth);
38878             var t = el.dom.offsetWidth; // force recalc
38879             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38880         }
38881     },
38882
38883     // private
38884     delayAutoWidth : function(){
38885         if(this.rendered){
38886             if(!this.awTask){
38887                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38888             }
38889             this.awTask.delay(20);
38890         }
38891     },
38892
38893     // private
38894     findTargetItem : function(e){
38895         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38896         if(t && t.menuItemId){
38897             return this.items.get(t.menuItemId);
38898         }
38899     },
38900
38901     // private
38902     onClick : function(e){
38903         Roo.log("menu.onClick");
38904         var t = this.findTargetItem(e);
38905         if(!t){
38906             return;
38907         }
38908         Roo.log(e);
38909         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38910             if(t == this.activeItem && t.shouldDeactivate(e)){
38911                 this.activeItem.deactivate();
38912                 delete this.activeItem;
38913                 return;
38914             }
38915             if(t.canActivate){
38916                 this.setActiveItem(t, true);
38917             }
38918             return;
38919             
38920             
38921         }
38922         
38923         t.onClick(e);
38924         this.fireEvent("click", this, t, e);
38925     },
38926
38927     // private
38928     setActiveItem : function(item, autoExpand){
38929         if(item != this.activeItem){
38930             if(this.activeItem){
38931                 this.activeItem.deactivate();
38932             }
38933             this.activeItem = item;
38934             item.activate(autoExpand);
38935         }else if(autoExpand){
38936             item.expandMenu();
38937         }
38938     },
38939
38940     // private
38941     tryActivate : function(start, step){
38942         var items = this.items;
38943         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38944             var item = items.get(i);
38945             if(!item.disabled && item.canActivate){
38946                 this.setActiveItem(item, false);
38947                 return item;
38948             }
38949         }
38950         return false;
38951     },
38952
38953     // private
38954     onMouseOver : function(e){
38955         var t;
38956         if(t = this.findTargetItem(e)){
38957             if(t.canActivate && !t.disabled){
38958                 this.setActiveItem(t, true);
38959             }
38960         }
38961         this.fireEvent("mouseover", this, e, t);
38962     },
38963
38964     // private
38965     onMouseOut : function(e){
38966         var t;
38967         if(t = this.findTargetItem(e)){
38968             if(t == this.activeItem && t.shouldDeactivate(e)){
38969                 this.activeItem.deactivate();
38970                 delete this.activeItem;
38971             }
38972         }
38973         this.fireEvent("mouseout", this, e, t);
38974     },
38975
38976     /**
38977      * Read-only.  Returns true if the menu is currently displayed, else false.
38978      * @type Boolean
38979      */
38980     isVisible : function(){
38981         return this.el && !this.hidden;
38982     },
38983
38984     /**
38985      * Displays this menu relative to another element
38986      * @param {String/HTMLElement/Roo.Element} element The element to align to
38987      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38988      * the element (defaults to this.defaultAlign)
38989      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38990      */
38991     show : function(el, pos, parentMenu){
38992         this.parentMenu = parentMenu;
38993         if(!this.el){
38994             this.render();
38995         }
38996         this.fireEvent("beforeshow", this);
38997         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38998     },
38999
39000     /**
39001      * Displays this menu at a specific xy position
39002      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39003      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39004      */
39005     showAt : function(xy, parentMenu, /* private: */_e){
39006         this.parentMenu = parentMenu;
39007         if(!this.el){
39008             this.render();
39009         }
39010         if(_e !== false){
39011             this.fireEvent("beforeshow", this);
39012             xy = this.el.adjustForConstraints(xy);
39013         }
39014         this.el.setXY(xy);
39015         this.el.show();
39016         this.hidden = false;
39017         this.focus();
39018         this.fireEvent("show", this);
39019     },
39020
39021     focus : function(){
39022         if(!this.hidden){
39023             this.doFocus.defer(50, this);
39024         }
39025     },
39026
39027     doFocus : function(){
39028         if(!this.hidden){
39029             this.focusEl.focus();
39030         }
39031     },
39032
39033     /**
39034      * Hides this menu and optionally all parent menus
39035      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39036      */
39037     hide : function(deep){
39038         if(this.el && this.isVisible()){
39039             this.fireEvent("beforehide", this);
39040             if(this.activeItem){
39041                 this.activeItem.deactivate();
39042                 this.activeItem = null;
39043             }
39044             this.el.hide();
39045             this.hidden = true;
39046             this.fireEvent("hide", this);
39047         }
39048         if(deep === true && this.parentMenu){
39049             this.parentMenu.hide(true);
39050         }
39051     },
39052
39053     /**
39054      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39055      * Any of the following are valid:
39056      * <ul>
39057      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39058      * <li>An HTMLElement object which will be converted to a menu item</li>
39059      * <li>A menu item config object that will be created as a new menu item</li>
39060      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39061      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39062      * </ul>
39063      * Usage:
39064      * <pre><code>
39065 // Create the menu
39066 var menu = new Roo.menu.Menu();
39067
39068 // Create a menu item to add by reference
39069 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39070
39071 // Add a bunch of items at once using different methods.
39072 // Only the last item added will be returned.
39073 var item = menu.add(
39074     menuItem,                // add existing item by ref
39075     'Dynamic Item',          // new TextItem
39076     '-',                     // new separator
39077     { text: 'Config Item' }  // new item by config
39078 );
39079 </code></pre>
39080      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39081      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39082      */
39083     add : function(){
39084         var a = arguments, l = a.length, item;
39085         for(var i = 0; i < l; i++){
39086             var el = a[i];
39087             if ((typeof(el) == "object") && el.xtype && el.xns) {
39088                 el = Roo.factory(el, Roo.menu);
39089             }
39090             
39091             if(el.render){ // some kind of Item
39092                 item = this.addItem(el);
39093             }else if(typeof el == "string"){ // string
39094                 if(el == "separator" || el == "-"){
39095                     item = this.addSeparator();
39096                 }else{
39097                     item = this.addText(el);
39098                 }
39099             }else if(el.tagName || el.el){ // element
39100                 item = this.addElement(el);
39101             }else if(typeof el == "object"){ // must be menu item config?
39102                 item = this.addMenuItem(el);
39103             }
39104         }
39105         return item;
39106     },
39107
39108     /**
39109      * Returns this menu's underlying {@link Roo.Element} object
39110      * @return {Roo.Element} The element
39111      */
39112     getEl : function(){
39113         if(!this.el){
39114             this.render();
39115         }
39116         return this.el;
39117     },
39118
39119     /**
39120      * Adds a separator bar to the menu
39121      * @return {Roo.menu.Item} The menu item that was added
39122      */
39123     addSeparator : function(){
39124         return this.addItem(new Roo.menu.Separator());
39125     },
39126
39127     /**
39128      * Adds an {@link Roo.Element} object to the menu
39129      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39130      * @return {Roo.menu.Item} The menu item that was added
39131      */
39132     addElement : function(el){
39133         return this.addItem(new Roo.menu.BaseItem(el));
39134     },
39135
39136     /**
39137      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39138      * @param {Roo.menu.Item} item The menu item to add
39139      * @return {Roo.menu.Item} The menu item that was added
39140      */
39141     addItem : function(item){
39142         this.items.add(item);
39143         if(this.ul){
39144             var li = document.createElement("li");
39145             li.className = "x-menu-list-item";
39146             this.ul.dom.appendChild(li);
39147             item.render(li, this);
39148             this.delayAutoWidth();
39149         }
39150         return item;
39151     },
39152
39153     /**
39154      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39155      * @param {Object} config A MenuItem config object
39156      * @return {Roo.menu.Item} The menu item that was added
39157      */
39158     addMenuItem : function(config){
39159         if(!(config instanceof Roo.menu.Item)){
39160             if(typeof config.checked == "boolean"){ // must be check menu item config?
39161                 config = new Roo.menu.CheckItem(config);
39162             }else{
39163                 config = new Roo.menu.Item(config);
39164             }
39165         }
39166         return this.addItem(config);
39167     },
39168
39169     /**
39170      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39171      * @param {String} text The text to display in the menu item
39172      * @return {Roo.menu.Item} The menu item that was added
39173      */
39174     addText : function(text){
39175         return this.addItem(new Roo.menu.TextItem({ text : text }));
39176     },
39177
39178     /**
39179      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39180      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39181      * @param {Roo.menu.Item} item The menu item to add
39182      * @return {Roo.menu.Item} The menu item that was added
39183      */
39184     insert : function(index, item){
39185         this.items.insert(index, item);
39186         if(this.ul){
39187             var li = document.createElement("li");
39188             li.className = "x-menu-list-item";
39189             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39190             item.render(li, this);
39191             this.delayAutoWidth();
39192         }
39193         return item;
39194     },
39195
39196     /**
39197      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39198      * @param {Roo.menu.Item} item The menu item to remove
39199      */
39200     remove : function(item){
39201         this.items.removeKey(item.id);
39202         item.destroy();
39203     },
39204
39205     /**
39206      * Removes and destroys all items in the menu
39207      */
39208     removeAll : function(){
39209         var f;
39210         while(f = this.items.first()){
39211             this.remove(f);
39212         }
39213     }
39214 });
39215
39216 // MenuNav is a private utility class used internally by the Menu
39217 Roo.menu.MenuNav = function(menu){
39218     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39219     this.scope = this.menu = menu;
39220 };
39221
39222 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39223     doRelay : function(e, h){
39224         var k = e.getKey();
39225         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39226             this.menu.tryActivate(0, 1);
39227             return false;
39228         }
39229         return h.call(this.scope || this, e, this.menu);
39230     },
39231
39232     up : function(e, m){
39233         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39234             m.tryActivate(m.items.length-1, -1);
39235         }
39236     },
39237
39238     down : function(e, m){
39239         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39240             m.tryActivate(0, 1);
39241         }
39242     },
39243
39244     right : function(e, m){
39245         if(m.activeItem){
39246             m.activeItem.expandMenu(true);
39247         }
39248     },
39249
39250     left : function(e, m){
39251         m.hide();
39252         if(m.parentMenu && m.parentMenu.activeItem){
39253             m.parentMenu.activeItem.activate();
39254         }
39255     },
39256
39257     enter : function(e, m){
39258         if(m.activeItem){
39259             e.stopPropagation();
39260             m.activeItem.onClick(e);
39261             m.fireEvent("click", this, m.activeItem);
39262             return true;
39263         }
39264     }
39265 });/*
39266  * Based on:
39267  * Ext JS Library 1.1.1
39268  * Copyright(c) 2006-2007, Ext JS, LLC.
39269  *
39270  * Originally Released Under LGPL - original licence link has changed is not relivant.
39271  *
39272  * Fork - LGPL
39273  * <script type="text/javascript">
39274  */
39275  
39276 /**
39277  * @class Roo.menu.MenuMgr
39278  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39279  * @static
39280  */
39281 Roo.menu.MenuMgr = function(){
39282    var menus, active, groups = {}, attached = false, lastShow = new Date();
39283
39284    // private - called when first menu is created
39285    function init(){
39286        menus = {};
39287        active = new Roo.util.MixedCollection();
39288        Roo.get(document).addKeyListener(27, function(){
39289            if(active.length > 0){
39290                hideAll();
39291            }
39292        });
39293    }
39294
39295    // private
39296    function hideAll(){
39297        if(active && active.length > 0){
39298            var c = active.clone();
39299            c.each(function(m){
39300                m.hide();
39301            });
39302        }
39303    }
39304
39305    // private
39306    function onHide(m){
39307        active.remove(m);
39308        if(active.length < 1){
39309            Roo.get(document).un("mousedown", onMouseDown);
39310            attached = false;
39311        }
39312    }
39313
39314    // private
39315    function onShow(m){
39316        var last = active.last();
39317        lastShow = new Date();
39318        active.add(m);
39319        if(!attached){
39320            Roo.get(document).on("mousedown", onMouseDown);
39321            attached = true;
39322        }
39323        if(m.parentMenu){
39324           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39325           m.parentMenu.activeChild = m;
39326        }else if(last && last.isVisible()){
39327           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39328        }
39329    }
39330
39331    // private
39332    function onBeforeHide(m){
39333        if(m.activeChild){
39334            m.activeChild.hide();
39335        }
39336        if(m.autoHideTimer){
39337            clearTimeout(m.autoHideTimer);
39338            delete m.autoHideTimer;
39339        }
39340    }
39341
39342    // private
39343    function onBeforeShow(m){
39344        var pm = m.parentMenu;
39345        if(!pm && !m.allowOtherMenus){
39346            hideAll();
39347        }else if(pm && pm.activeChild && active != m){
39348            pm.activeChild.hide();
39349        }
39350    }
39351
39352    // private
39353    function onMouseDown(e){
39354        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39355            hideAll();
39356        }
39357    }
39358
39359    // private
39360    function onBeforeCheck(mi, state){
39361        if(state){
39362            var g = groups[mi.group];
39363            for(var i = 0, l = g.length; i < l; i++){
39364                if(g[i] != mi){
39365                    g[i].setChecked(false);
39366                }
39367            }
39368        }
39369    }
39370
39371    return {
39372
39373        /**
39374         * Hides all menus that are currently visible
39375         */
39376        hideAll : function(){
39377             hideAll();  
39378        },
39379
39380        // private
39381        register : function(menu){
39382            if(!menus){
39383                init();
39384            }
39385            menus[menu.id] = menu;
39386            menu.on("beforehide", onBeforeHide);
39387            menu.on("hide", onHide);
39388            menu.on("beforeshow", onBeforeShow);
39389            menu.on("show", onShow);
39390            var g = menu.group;
39391            if(g && menu.events["checkchange"]){
39392                if(!groups[g]){
39393                    groups[g] = [];
39394                }
39395                groups[g].push(menu);
39396                menu.on("checkchange", onCheck);
39397            }
39398        },
39399
39400         /**
39401          * Returns a {@link Roo.menu.Menu} object
39402          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39403          * be used to generate and return a new Menu instance.
39404          */
39405        get : function(menu){
39406            if(typeof menu == "string"){ // menu id
39407                return menus[menu];
39408            }else if(menu.events){  // menu instance
39409                return menu;
39410            }else if(typeof menu.length == 'number'){ // array of menu items?
39411                return new Roo.menu.Menu({items:menu});
39412            }else{ // otherwise, must be a config
39413                return new Roo.menu.Menu(menu);
39414            }
39415        },
39416
39417        // private
39418        unregister : function(menu){
39419            delete menus[menu.id];
39420            menu.un("beforehide", onBeforeHide);
39421            menu.un("hide", onHide);
39422            menu.un("beforeshow", onBeforeShow);
39423            menu.un("show", onShow);
39424            var g = menu.group;
39425            if(g && menu.events["checkchange"]){
39426                groups[g].remove(menu);
39427                menu.un("checkchange", onCheck);
39428            }
39429        },
39430
39431        // private
39432        registerCheckable : function(menuItem){
39433            var g = menuItem.group;
39434            if(g){
39435                if(!groups[g]){
39436                    groups[g] = [];
39437                }
39438                groups[g].push(menuItem);
39439                menuItem.on("beforecheckchange", onBeforeCheck);
39440            }
39441        },
39442
39443        // private
39444        unregisterCheckable : function(menuItem){
39445            var g = menuItem.group;
39446            if(g){
39447                groups[g].remove(menuItem);
39448                menuItem.un("beforecheckchange", onBeforeCheck);
39449            }
39450        }
39451    };
39452 }();/*
39453  * Based on:
39454  * Ext JS Library 1.1.1
39455  * Copyright(c) 2006-2007, Ext JS, LLC.
39456  *
39457  * Originally Released Under LGPL - original licence link has changed is not relivant.
39458  *
39459  * Fork - LGPL
39460  * <script type="text/javascript">
39461  */
39462  
39463
39464 /**
39465  * @class Roo.menu.BaseItem
39466  * @extends Roo.Component
39467  * @abstract
39468  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39469  * management and base configuration options shared by all menu components.
39470  * @constructor
39471  * Creates a new BaseItem
39472  * @param {Object} config Configuration options
39473  */
39474 Roo.menu.BaseItem = function(config){
39475     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39476
39477     this.addEvents({
39478         /**
39479          * @event click
39480          * Fires when this item is clicked
39481          * @param {Roo.menu.BaseItem} this
39482          * @param {Roo.EventObject} e
39483          */
39484         click: true,
39485         /**
39486          * @event activate
39487          * Fires when this item is activated
39488          * @param {Roo.menu.BaseItem} this
39489          */
39490         activate : true,
39491         /**
39492          * @event deactivate
39493          * Fires when this item is deactivated
39494          * @param {Roo.menu.BaseItem} this
39495          */
39496         deactivate : true
39497     });
39498
39499     if(this.handler){
39500         this.on("click", this.handler, this.scope, true);
39501     }
39502 };
39503
39504 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39505     /**
39506      * @cfg {Function} handler
39507      * A function that will handle the click event of this menu item (defaults to undefined)
39508      */
39509     /**
39510      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39511      */
39512     canActivate : false,
39513     
39514      /**
39515      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39516      */
39517     hidden: false,
39518     
39519     /**
39520      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39521      */
39522     activeClass : "x-menu-item-active",
39523     /**
39524      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39525      */
39526     hideOnClick : true,
39527     /**
39528      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39529      */
39530     hideDelay : 100,
39531
39532     // private
39533     ctype: "Roo.menu.BaseItem",
39534
39535     // private
39536     actionMode : "container",
39537
39538     // private
39539     render : function(container, parentMenu){
39540         this.parentMenu = parentMenu;
39541         Roo.menu.BaseItem.superclass.render.call(this, container);
39542         this.container.menuItemId = this.id;
39543     },
39544
39545     // private
39546     onRender : function(container, position){
39547         this.el = Roo.get(this.el);
39548         container.dom.appendChild(this.el.dom);
39549     },
39550
39551     // private
39552     onClick : function(e){
39553         if(!this.disabled && this.fireEvent("click", this, e) !== false
39554                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39555             this.handleClick(e);
39556         }else{
39557             e.stopEvent();
39558         }
39559     },
39560
39561     // private
39562     activate : function(){
39563         if(this.disabled){
39564             return false;
39565         }
39566         var li = this.container;
39567         li.addClass(this.activeClass);
39568         this.region = li.getRegion().adjust(2, 2, -2, -2);
39569         this.fireEvent("activate", this);
39570         return true;
39571     },
39572
39573     // private
39574     deactivate : function(){
39575         this.container.removeClass(this.activeClass);
39576         this.fireEvent("deactivate", this);
39577     },
39578
39579     // private
39580     shouldDeactivate : function(e){
39581         return !this.region || !this.region.contains(e.getPoint());
39582     },
39583
39584     // private
39585     handleClick : function(e){
39586         if(this.hideOnClick){
39587             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39588         }
39589     },
39590
39591     // private
39592     expandMenu : function(autoActivate){
39593         // do nothing
39594     },
39595
39596     // private
39597     hideMenu : function(){
39598         // do nothing
39599     }
39600 });/*
39601  * Based on:
39602  * Ext JS Library 1.1.1
39603  * Copyright(c) 2006-2007, Ext JS, LLC.
39604  *
39605  * Originally Released Under LGPL - original licence link has changed is not relivant.
39606  *
39607  * Fork - LGPL
39608  * <script type="text/javascript">
39609  */
39610  
39611 /**
39612  * @class Roo.menu.Adapter
39613  * @extends Roo.menu.BaseItem
39614  * @abstract
39615  * 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.
39616  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39617  * @constructor
39618  * Creates a new Adapter
39619  * @param {Object} config Configuration options
39620  */
39621 Roo.menu.Adapter = function(component, config){
39622     Roo.menu.Adapter.superclass.constructor.call(this, config);
39623     this.component = component;
39624 };
39625 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39626     // private
39627     canActivate : true,
39628
39629     // private
39630     onRender : function(container, position){
39631         this.component.render(container);
39632         this.el = this.component.getEl();
39633     },
39634
39635     // private
39636     activate : function(){
39637         if(this.disabled){
39638             return false;
39639         }
39640         this.component.focus();
39641         this.fireEvent("activate", this);
39642         return true;
39643     },
39644
39645     // private
39646     deactivate : function(){
39647         this.fireEvent("deactivate", this);
39648     },
39649
39650     // private
39651     disable : function(){
39652         this.component.disable();
39653         Roo.menu.Adapter.superclass.disable.call(this);
39654     },
39655
39656     // private
39657     enable : function(){
39658         this.component.enable();
39659         Roo.menu.Adapter.superclass.enable.call(this);
39660     }
39661 });/*
39662  * Based on:
39663  * Ext JS Library 1.1.1
39664  * Copyright(c) 2006-2007, Ext JS, LLC.
39665  *
39666  * Originally Released Under LGPL - original licence link has changed is not relivant.
39667  *
39668  * Fork - LGPL
39669  * <script type="text/javascript">
39670  */
39671
39672 /**
39673  * @class Roo.menu.TextItem
39674  * @extends Roo.menu.BaseItem
39675  * Adds a static text string to a menu, usually used as either a heading or group separator.
39676  * Note: old style constructor with text is still supported.
39677  * 
39678  * @constructor
39679  * Creates a new TextItem
39680  * @param {Object} cfg Configuration
39681  */
39682 Roo.menu.TextItem = function(cfg){
39683     if (typeof(cfg) == 'string') {
39684         this.text = cfg;
39685     } else {
39686         Roo.apply(this,cfg);
39687     }
39688     
39689     Roo.menu.TextItem.superclass.constructor.call(this);
39690 };
39691
39692 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39693     /**
39694      * @cfg {String} text Text to show on item.
39695      */
39696     text : '',
39697     
39698     /**
39699      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39700      */
39701     hideOnClick : false,
39702     /**
39703      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39704      */
39705     itemCls : "x-menu-text",
39706
39707     // private
39708     onRender : function(){
39709         var s = document.createElement("span");
39710         s.className = this.itemCls;
39711         s.innerHTML = this.text;
39712         this.el = s;
39713         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39714     }
39715 });/*
39716  * Based on:
39717  * Ext JS Library 1.1.1
39718  * Copyright(c) 2006-2007, Ext JS, LLC.
39719  *
39720  * Originally Released Under LGPL - original licence link has changed is not relivant.
39721  *
39722  * Fork - LGPL
39723  * <script type="text/javascript">
39724  */
39725
39726 /**
39727  * @class Roo.menu.Separator
39728  * @extends Roo.menu.BaseItem
39729  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39730  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39731  * @constructor
39732  * @param {Object} config Configuration options
39733  */
39734 Roo.menu.Separator = function(config){
39735     Roo.menu.Separator.superclass.constructor.call(this, config);
39736 };
39737
39738 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39739     /**
39740      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39741      */
39742     itemCls : "x-menu-sep",
39743     /**
39744      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39745      */
39746     hideOnClick : false,
39747
39748     // private
39749     onRender : function(li){
39750         var s = document.createElement("span");
39751         s.className = this.itemCls;
39752         s.innerHTML = "&#160;";
39753         this.el = s;
39754         li.addClass("x-menu-sep-li");
39755         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39756     }
39757 });/*
39758  * Based on:
39759  * Ext JS Library 1.1.1
39760  * Copyright(c) 2006-2007, Ext JS, LLC.
39761  *
39762  * Originally Released Under LGPL - original licence link has changed is not relivant.
39763  *
39764  * Fork - LGPL
39765  * <script type="text/javascript">
39766  */
39767 /**
39768  * @class Roo.menu.Item
39769  * @extends Roo.menu.BaseItem
39770  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39771  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39772  * activation and click handling.
39773  * @constructor
39774  * Creates a new Item
39775  * @param {Object} config Configuration options
39776  */
39777 Roo.menu.Item = function(config){
39778     Roo.menu.Item.superclass.constructor.call(this, config);
39779     if(this.menu){
39780         this.menu = Roo.menu.MenuMgr.get(this.menu);
39781     }
39782 };
39783 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39784     /**
39785      * @cfg {Roo.menu.Menu} menu
39786      * A Sub menu
39787      */
39788     /**
39789      * @cfg {String} text
39790      * The text to show on the menu item.
39791      */
39792     text: '',
39793      /**
39794      * @cfg {String} HTML to render in menu
39795      * The text to show on the menu item (HTML version).
39796      */
39797     html: '',
39798     /**
39799      * @cfg {String} icon
39800      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39801      */
39802     icon: undefined,
39803     /**
39804      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39805      */
39806     itemCls : "x-menu-item",
39807     /**
39808      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39809      */
39810     canActivate : true,
39811     /**
39812      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39813      */
39814     showDelay: 200,
39815     // doc'd in BaseItem
39816     hideDelay: 200,
39817
39818     // private
39819     ctype: "Roo.menu.Item",
39820     
39821     // private
39822     onRender : function(container, position){
39823         var el = document.createElement("a");
39824         el.hideFocus = true;
39825         el.unselectable = "on";
39826         el.href = this.href || "#";
39827         if(this.hrefTarget){
39828             el.target = this.hrefTarget;
39829         }
39830         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39831         
39832         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39833         
39834         el.innerHTML = String.format(
39835                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39836                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39837         this.el = el;
39838         Roo.menu.Item.superclass.onRender.call(this, container, position);
39839     },
39840
39841     /**
39842      * Sets the text to display in this menu item
39843      * @param {String} text The text to display
39844      * @param {Boolean} isHTML true to indicate text is pure html.
39845      */
39846     setText : function(text, isHTML){
39847         if (isHTML) {
39848             this.html = text;
39849         } else {
39850             this.text = text;
39851             this.html = '';
39852         }
39853         if(this.rendered){
39854             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39855      
39856             this.el.update(String.format(
39857                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39858                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39859             this.parentMenu.autoWidth();
39860         }
39861     },
39862
39863     // private
39864     handleClick : function(e){
39865         if(!this.href){ // if no link defined, stop the event automatically
39866             e.stopEvent();
39867         }
39868         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39869     },
39870
39871     // private
39872     activate : function(autoExpand){
39873         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39874             this.focus();
39875             if(autoExpand){
39876                 this.expandMenu();
39877             }
39878         }
39879         return true;
39880     },
39881
39882     // private
39883     shouldDeactivate : function(e){
39884         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39885             if(this.menu && this.menu.isVisible()){
39886                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39887             }
39888             return true;
39889         }
39890         return false;
39891     },
39892
39893     // private
39894     deactivate : function(){
39895         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39896         this.hideMenu();
39897     },
39898
39899     // private
39900     expandMenu : function(autoActivate){
39901         if(!this.disabled && this.menu){
39902             clearTimeout(this.hideTimer);
39903             delete this.hideTimer;
39904             if(!this.menu.isVisible() && !this.showTimer){
39905                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39906             }else if (this.menu.isVisible() && autoActivate){
39907                 this.menu.tryActivate(0, 1);
39908             }
39909         }
39910     },
39911
39912     // private
39913     deferExpand : function(autoActivate){
39914         delete this.showTimer;
39915         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39916         if(autoActivate){
39917             this.menu.tryActivate(0, 1);
39918         }
39919     },
39920
39921     // private
39922     hideMenu : function(){
39923         clearTimeout(this.showTimer);
39924         delete this.showTimer;
39925         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39926             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39927         }
39928     },
39929
39930     // private
39931     deferHide : function(){
39932         delete this.hideTimer;
39933         this.menu.hide();
39934     }
39935 });/*
39936  * Based on:
39937  * Ext JS Library 1.1.1
39938  * Copyright(c) 2006-2007, Ext JS, LLC.
39939  *
39940  * Originally Released Under LGPL - original licence link has changed is not relivant.
39941  *
39942  * Fork - LGPL
39943  * <script type="text/javascript">
39944  */
39945  
39946 /**
39947  * @class Roo.menu.CheckItem
39948  * @extends Roo.menu.Item
39949  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39950  * @constructor
39951  * Creates a new CheckItem
39952  * @param {Object} config Configuration options
39953  */
39954 Roo.menu.CheckItem = function(config){
39955     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39956     this.addEvents({
39957         /**
39958          * @event beforecheckchange
39959          * Fires before the checked value is set, providing an opportunity to cancel if needed
39960          * @param {Roo.menu.CheckItem} this
39961          * @param {Boolean} checked The new checked value that will be set
39962          */
39963         "beforecheckchange" : true,
39964         /**
39965          * @event checkchange
39966          * Fires after the checked value has been set
39967          * @param {Roo.menu.CheckItem} this
39968          * @param {Boolean} checked The checked value that was set
39969          */
39970         "checkchange" : true
39971     });
39972     if(this.checkHandler){
39973         this.on('checkchange', this.checkHandler, this.scope);
39974     }
39975 };
39976 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39977     /**
39978      * @cfg {String} group
39979      * All check items with the same group name will automatically be grouped into a single-select
39980      * radio button group (defaults to '')
39981      */
39982     /**
39983      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39984      */
39985     itemCls : "x-menu-item x-menu-check-item",
39986     /**
39987      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39988      */
39989     groupClass : "x-menu-group-item",
39990
39991     /**
39992      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39993      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39994      * initialized with checked = true will be rendered as checked.
39995      */
39996     checked: false,
39997
39998     // private
39999     ctype: "Roo.menu.CheckItem",
40000
40001     // private
40002     onRender : function(c){
40003         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40004         if(this.group){
40005             this.el.addClass(this.groupClass);
40006         }
40007         Roo.menu.MenuMgr.registerCheckable(this);
40008         if(this.checked){
40009             this.checked = false;
40010             this.setChecked(true, true);
40011         }
40012     },
40013
40014     // private
40015     destroy : function(){
40016         if(this.rendered){
40017             Roo.menu.MenuMgr.unregisterCheckable(this);
40018         }
40019         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40020     },
40021
40022     /**
40023      * Set the checked state of this item
40024      * @param {Boolean} checked The new checked value
40025      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40026      */
40027     setChecked : function(state, suppressEvent){
40028         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40029             if(this.container){
40030                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40031             }
40032             this.checked = state;
40033             if(suppressEvent !== true){
40034                 this.fireEvent("checkchange", this, state);
40035             }
40036         }
40037     },
40038
40039     // private
40040     handleClick : function(e){
40041        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40042            this.setChecked(!this.checked);
40043        }
40044        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40045     }
40046 });/*
40047  * Based on:
40048  * Ext JS Library 1.1.1
40049  * Copyright(c) 2006-2007, Ext JS, LLC.
40050  *
40051  * Originally Released Under LGPL - original licence link has changed is not relivant.
40052  *
40053  * Fork - LGPL
40054  * <script type="text/javascript">
40055  */
40056  
40057 /**
40058  * @class Roo.menu.DateItem
40059  * @extends Roo.menu.Adapter
40060  * A menu item that wraps the {@link Roo.DatPicker} component.
40061  * @constructor
40062  * Creates a new DateItem
40063  * @param {Object} config Configuration options
40064  */
40065 Roo.menu.DateItem = function(config){
40066     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40067     /** The Roo.DatePicker object @type Roo.DatePicker */
40068     this.picker = this.component;
40069     this.addEvents({select: true});
40070     
40071     this.picker.on("render", function(picker){
40072         picker.getEl().swallowEvent("click");
40073         picker.container.addClass("x-menu-date-item");
40074     });
40075
40076     this.picker.on("select", this.onSelect, this);
40077 };
40078
40079 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40080     // private
40081     onSelect : function(picker, date){
40082         this.fireEvent("select", this, date, picker);
40083         Roo.menu.DateItem.superclass.handleClick.call(this);
40084     }
40085 });/*
40086  * Based on:
40087  * Ext JS Library 1.1.1
40088  * Copyright(c) 2006-2007, Ext JS, LLC.
40089  *
40090  * Originally Released Under LGPL - original licence link has changed is not relivant.
40091  *
40092  * Fork - LGPL
40093  * <script type="text/javascript">
40094  */
40095  
40096 /**
40097  * @class Roo.menu.ColorItem
40098  * @extends Roo.menu.Adapter
40099  * A menu item that wraps the {@link Roo.ColorPalette} component.
40100  * @constructor
40101  * Creates a new ColorItem
40102  * @param {Object} config Configuration options
40103  */
40104 Roo.menu.ColorItem = function(config){
40105     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40106     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40107     this.palette = this.component;
40108     this.relayEvents(this.palette, ["select"]);
40109     if(this.selectHandler){
40110         this.on('select', this.selectHandler, this.scope);
40111     }
40112 };
40113 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40114  * Based on:
40115  * Ext JS Library 1.1.1
40116  * Copyright(c) 2006-2007, Ext JS, LLC.
40117  *
40118  * Originally Released Under LGPL - original licence link has changed is not relivant.
40119  *
40120  * Fork - LGPL
40121  * <script type="text/javascript">
40122  */
40123  
40124
40125 /**
40126  * @class Roo.menu.DateMenu
40127  * @extends Roo.menu.Menu
40128  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40129  * @constructor
40130  * Creates a new DateMenu
40131  * @param {Object} config Configuration options
40132  */
40133 Roo.menu.DateMenu = function(config){
40134     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40135     this.plain = true;
40136     var di = new Roo.menu.DateItem(config);
40137     this.add(di);
40138     /**
40139      * The {@link Roo.DatePicker} instance for this DateMenu
40140      * @type DatePicker
40141      */
40142     this.picker = di.picker;
40143     /**
40144      * @event select
40145      * @param {DatePicker} picker
40146      * @param {Date} date
40147      */
40148     this.relayEvents(di, ["select"]);
40149     this.on('beforeshow', function(){
40150         if(this.picker){
40151             this.picker.hideMonthPicker(false);
40152         }
40153     }, this);
40154 };
40155 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40156     cls:'x-date-menu'
40157 });/*
40158  * Based on:
40159  * Ext JS Library 1.1.1
40160  * Copyright(c) 2006-2007, Ext JS, LLC.
40161  *
40162  * Originally Released Under LGPL - original licence link has changed is not relivant.
40163  *
40164  * Fork - LGPL
40165  * <script type="text/javascript">
40166  */
40167  
40168
40169 /**
40170  * @class Roo.menu.ColorMenu
40171  * @extends Roo.menu.Menu
40172  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40173  * @constructor
40174  * Creates a new ColorMenu
40175  * @param {Object} config Configuration options
40176  */
40177 Roo.menu.ColorMenu = function(config){
40178     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40179     this.plain = true;
40180     var ci = new Roo.menu.ColorItem(config);
40181     this.add(ci);
40182     /**
40183      * The {@link Roo.ColorPalette} instance for this ColorMenu
40184      * @type ColorPalette
40185      */
40186     this.palette = ci.palette;
40187     /**
40188      * @event select
40189      * @param {ColorPalette} palette
40190      * @param {String} color
40191      */
40192     this.relayEvents(ci, ["select"]);
40193 };
40194 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40195  * Based on:
40196  * Ext JS Library 1.1.1
40197  * Copyright(c) 2006-2007, Ext JS, LLC.
40198  *
40199  * Originally Released Under LGPL - original licence link has changed is not relivant.
40200  *
40201  * Fork - LGPL
40202  * <script type="text/javascript">
40203  */
40204  
40205 /**
40206  * @class Roo.form.TextItem
40207  * @extends Roo.BoxComponent
40208  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40209  * @constructor
40210  * Creates a new TextItem
40211  * @param {Object} config Configuration options
40212  */
40213 Roo.form.TextItem = function(config){
40214     Roo.form.TextItem.superclass.constructor.call(this, config);
40215 };
40216
40217 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40218     
40219     /**
40220      * @cfg {String} tag the tag for this item (default div)
40221      */
40222     tag : 'div',
40223     /**
40224      * @cfg {String} html the content for this item
40225      */
40226     html : '',
40227     
40228     getAutoCreate : function()
40229     {
40230         var cfg = {
40231             id: this.id,
40232             tag: this.tag,
40233             html: this.html,
40234             cls: 'x-form-item'
40235         };
40236         
40237         return cfg;
40238         
40239     },
40240     
40241     onRender : function(ct, position)
40242     {
40243         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40244         
40245         if(!this.el){
40246             var cfg = this.getAutoCreate();
40247             if(!cfg.name){
40248                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40249             }
40250             if (!cfg.name.length) {
40251                 delete cfg.name;
40252             }
40253             this.el = ct.createChild(cfg, position);
40254         }
40255     },
40256     /*
40257      * setHTML
40258      * @param {String} html update the Contents of the element.
40259      */
40260     setHTML : function(html)
40261     {
40262         this.fieldEl.dom.innerHTML = html;
40263     }
40264     
40265 });/*
40266  * Based on:
40267  * Ext JS Library 1.1.1
40268  * Copyright(c) 2006-2007, Ext JS, LLC.
40269  *
40270  * Originally Released Under LGPL - original licence link has changed is not relivant.
40271  *
40272  * Fork - LGPL
40273  * <script type="text/javascript">
40274  */
40275  
40276 /**
40277  * @class Roo.form.Field
40278  * @extends Roo.BoxComponent
40279  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40280  * @constructor
40281  * Creates a new Field
40282  * @param {Object} config Configuration options
40283  */
40284 Roo.form.Field = function(config){
40285     Roo.form.Field.superclass.constructor.call(this, config);
40286 };
40287
40288 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40289     /**
40290      * @cfg {String} fieldLabel Label to use when rendering a form.
40291      */
40292        /**
40293      * @cfg {String} qtip Mouse over tip
40294      */
40295      
40296     /**
40297      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40298      */
40299     invalidClass : "x-form-invalid",
40300     /**
40301      * @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")
40302      */
40303     invalidText : "The value in this field is invalid",
40304     /**
40305      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40306      */
40307     focusClass : "x-form-focus",
40308     /**
40309      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40310       automatic validation (defaults to "keyup").
40311      */
40312     validationEvent : "keyup",
40313     /**
40314      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40315      */
40316     validateOnBlur : true,
40317     /**
40318      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40319      */
40320     validationDelay : 250,
40321     /**
40322      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40323      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40324      */
40325     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40326     /**
40327      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40328      */
40329     fieldClass : "x-form-field",
40330     /**
40331      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40332      *<pre>
40333 Value         Description
40334 -----------   ----------------------------------------------------------------------
40335 qtip          Display a quick tip when the user hovers over the field
40336 title         Display a default browser title attribute popup
40337 under         Add a block div beneath the field containing the error text
40338 side          Add an error icon to the right of the field with a popup on hover
40339 [element id]  Add the error text directly to the innerHTML of the specified element
40340 </pre>
40341      */
40342     msgTarget : 'qtip',
40343     /**
40344      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40345      */
40346     msgFx : 'normal',
40347
40348     /**
40349      * @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.
40350      */
40351     readOnly : false,
40352
40353     /**
40354      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40355      */
40356     disabled : false,
40357
40358     /**
40359      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40360      */
40361     inputType : undefined,
40362     
40363     /**
40364      * @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).
40365          */
40366         tabIndex : undefined,
40367         
40368     // private
40369     isFormField : true,
40370
40371     // private
40372     hasFocus : false,
40373     /**
40374      * @property {Roo.Element} fieldEl
40375      * Element Containing the rendered Field (with label etc.)
40376      */
40377     /**
40378      * @cfg {Mixed} value A value to initialize this field with.
40379      */
40380     value : undefined,
40381
40382     /**
40383      * @cfg {String} name The field's HTML name attribute.
40384      */
40385     /**
40386      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40387      */
40388     // private
40389     loadedValue : false,
40390      
40391      
40392         // private ??
40393         initComponent : function(){
40394         Roo.form.Field.superclass.initComponent.call(this);
40395         this.addEvents({
40396             /**
40397              * @event focus
40398              * Fires when this field receives input focus.
40399              * @param {Roo.form.Field} this
40400              */
40401             focus : true,
40402             /**
40403              * @event blur
40404              * Fires when this field loses input focus.
40405              * @param {Roo.form.Field} this
40406              */
40407             blur : true,
40408             /**
40409              * @event specialkey
40410              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40411              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40412              * @param {Roo.form.Field} this
40413              * @param {Roo.EventObject} e The event object
40414              */
40415             specialkey : true,
40416             /**
40417              * @event change
40418              * Fires just before the field blurs if the field value has changed.
40419              * @param {Roo.form.Field} this
40420              * @param {Mixed} newValue The new value
40421              * @param {Mixed} oldValue The original value
40422              */
40423             change : true,
40424             /**
40425              * @event invalid
40426              * Fires after the field has been marked as invalid.
40427              * @param {Roo.form.Field} this
40428              * @param {String} msg The validation message
40429              */
40430             invalid : true,
40431             /**
40432              * @event valid
40433              * Fires after the field has been validated with no errors.
40434              * @param {Roo.form.Field} this
40435              */
40436             valid : true,
40437              /**
40438              * @event keyup
40439              * Fires after the key up
40440              * @param {Roo.form.Field} this
40441              * @param {Roo.EventObject}  e The event Object
40442              */
40443             keyup : true
40444         });
40445     },
40446
40447     /**
40448      * Returns the name attribute of the field if available
40449      * @return {String} name The field name
40450      */
40451     getName: function(){
40452          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40453     },
40454
40455     // private
40456     onRender : function(ct, position){
40457         Roo.form.Field.superclass.onRender.call(this, ct, position);
40458         if(!this.el){
40459             var cfg = this.getAutoCreate();
40460             if(!cfg.name){
40461                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40462             }
40463             if (!cfg.name.length) {
40464                 delete cfg.name;
40465             }
40466             if(this.inputType){
40467                 cfg.type = this.inputType;
40468             }
40469             this.el = ct.createChild(cfg, position);
40470         }
40471         var type = this.el.dom.type;
40472         if(type){
40473             if(type == 'password'){
40474                 type = 'text';
40475             }
40476             this.el.addClass('x-form-'+type);
40477         }
40478         if(this.readOnly){
40479             this.el.dom.readOnly = true;
40480         }
40481         if(this.tabIndex !== undefined){
40482             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40483         }
40484
40485         this.el.addClass([this.fieldClass, this.cls]);
40486         this.initValue();
40487     },
40488
40489     /**
40490      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40491      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40492      * @return {Roo.form.Field} this
40493      */
40494     applyTo : function(target){
40495         this.allowDomMove = false;
40496         this.el = Roo.get(target);
40497         this.render(this.el.dom.parentNode);
40498         return this;
40499     },
40500
40501     // private
40502     initValue : function(){
40503         if(this.value !== undefined){
40504             this.setValue(this.value);
40505         }else if(this.el.dom.value.length > 0){
40506             this.setValue(this.el.dom.value);
40507         }
40508     },
40509
40510     /**
40511      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40512      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40513      */
40514     isDirty : function() {
40515         if(this.disabled) {
40516             return false;
40517         }
40518         return String(this.getValue()) !== String(this.originalValue);
40519     },
40520
40521     /**
40522      * stores the current value in loadedValue
40523      */
40524     resetHasChanged : function()
40525     {
40526         this.loadedValue = String(this.getValue());
40527     },
40528     /**
40529      * checks the current value against the 'loaded' value.
40530      * Note - will return false if 'resetHasChanged' has not been called first.
40531      */
40532     hasChanged : function()
40533     {
40534         if(this.disabled || this.readOnly) {
40535             return false;
40536         }
40537         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40538     },
40539     
40540     
40541     
40542     // private
40543     afterRender : function(){
40544         Roo.form.Field.superclass.afterRender.call(this);
40545         this.initEvents();
40546     },
40547
40548     // private
40549     fireKey : function(e){
40550         //Roo.log('field ' + e.getKey());
40551         if(e.isNavKeyPress()){
40552             this.fireEvent("specialkey", this, e);
40553         }
40554     },
40555
40556     /**
40557      * Resets the current field value to the originally loaded value and clears any validation messages
40558      */
40559     reset : function(){
40560         this.setValue(this.resetValue);
40561         this.originalValue = this.getValue();
40562         this.clearInvalid();
40563     },
40564
40565     // private
40566     initEvents : function(){
40567         // safari killled keypress - so keydown is now used..
40568         this.el.on("keydown" , this.fireKey,  this);
40569         this.el.on("focus", this.onFocus,  this);
40570         this.el.on("blur", this.onBlur,  this);
40571         this.el.relayEvent('keyup', this);
40572
40573         // reference to original value for reset
40574         this.originalValue = this.getValue();
40575         this.resetValue =  this.getValue();
40576     },
40577
40578     // private
40579     onFocus : function(){
40580         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40581             this.el.addClass(this.focusClass);
40582         }
40583         if(!this.hasFocus){
40584             this.hasFocus = true;
40585             this.startValue = this.getValue();
40586             this.fireEvent("focus", this);
40587         }
40588     },
40589
40590     beforeBlur : Roo.emptyFn,
40591
40592     // private
40593     onBlur : function(){
40594         this.beforeBlur();
40595         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40596             this.el.removeClass(this.focusClass);
40597         }
40598         this.hasFocus = false;
40599         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40600             this.validate();
40601         }
40602         var v = this.getValue();
40603         if(String(v) !== String(this.startValue)){
40604             this.fireEvent('change', this, v, this.startValue);
40605         }
40606         this.fireEvent("blur", this);
40607     },
40608
40609     /**
40610      * Returns whether or not the field value is currently valid
40611      * @param {Boolean} preventMark True to disable marking the field invalid
40612      * @return {Boolean} True if the value is valid, else false
40613      */
40614     isValid : function(preventMark){
40615         if(this.disabled){
40616             return true;
40617         }
40618         var restore = this.preventMark;
40619         this.preventMark = preventMark === true;
40620         var v = this.validateValue(this.processValue(this.getRawValue()));
40621         this.preventMark = restore;
40622         return v;
40623     },
40624
40625     /**
40626      * Validates the field value
40627      * @return {Boolean} True if the value is valid, else false
40628      */
40629     validate : function(){
40630         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40631             this.clearInvalid();
40632             return true;
40633         }
40634         return false;
40635     },
40636
40637     processValue : function(value){
40638         return value;
40639     },
40640
40641     // private
40642     // Subclasses should provide the validation implementation by overriding this
40643     validateValue : function(value){
40644         return true;
40645     },
40646
40647     /**
40648      * Mark this field as invalid
40649      * @param {String} msg The validation message
40650      */
40651     markInvalid : function(msg){
40652         if(!this.rendered || this.preventMark){ // not rendered
40653             return;
40654         }
40655         
40656         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40657         
40658         obj.el.addClass(this.invalidClass);
40659         msg = msg || this.invalidText;
40660         switch(this.msgTarget){
40661             case 'qtip':
40662                 obj.el.dom.qtip = msg;
40663                 obj.el.dom.qclass = 'x-form-invalid-tip';
40664                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40665                     Roo.QuickTips.enable();
40666                 }
40667                 break;
40668             case 'title':
40669                 this.el.dom.title = msg;
40670                 break;
40671             case 'under':
40672                 if(!this.errorEl){
40673                     var elp = this.el.findParent('.x-form-element', 5, true);
40674                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40675                     this.errorEl.setWidth(elp.getWidth(true)-20);
40676                 }
40677                 this.errorEl.update(msg);
40678                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40679                 break;
40680             case 'side':
40681                 if(!this.errorIcon){
40682                     var elp = this.el.findParent('.x-form-element', 5, true);
40683                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40684                 }
40685                 this.alignErrorIcon();
40686                 this.errorIcon.dom.qtip = msg;
40687                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40688                 this.errorIcon.show();
40689                 this.on('resize', this.alignErrorIcon, this);
40690                 break;
40691             default:
40692                 var t = Roo.getDom(this.msgTarget);
40693                 t.innerHTML = msg;
40694                 t.style.display = this.msgDisplay;
40695                 break;
40696         }
40697         this.fireEvent('invalid', this, msg);
40698     },
40699
40700     // private
40701     alignErrorIcon : function(){
40702         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40703     },
40704
40705     /**
40706      * Clear any invalid styles/messages for this field
40707      */
40708     clearInvalid : function(){
40709         if(!this.rendered || this.preventMark){ // not rendered
40710             return;
40711         }
40712         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40713         
40714         obj.el.removeClass(this.invalidClass);
40715         switch(this.msgTarget){
40716             case 'qtip':
40717                 obj.el.dom.qtip = '';
40718                 break;
40719             case 'title':
40720                 this.el.dom.title = '';
40721                 break;
40722             case 'under':
40723                 if(this.errorEl){
40724                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40725                 }
40726                 break;
40727             case 'side':
40728                 if(this.errorIcon){
40729                     this.errorIcon.dom.qtip = '';
40730                     this.errorIcon.hide();
40731                     this.un('resize', this.alignErrorIcon, this);
40732                 }
40733                 break;
40734             default:
40735                 var t = Roo.getDom(this.msgTarget);
40736                 t.innerHTML = '';
40737                 t.style.display = 'none';
40738                 break;
40739         }
40740         this.fireEvent('valid', this);
40741     },
40742
40743     /**
40744      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40745      * @return {Mixed} value The field value
40746      */
40747     getRawValue : function(){
40748         var v = this.el.getValue();
40749         
40750         return v;
40751     },
40752
40753     /**
40754      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40755      * @return {Mixed} value The field value
40756      */
40757     getValue : function(){
40758         var v = this.el.getValue();
40759          
40760         return v;
40761     },
40762
40763     /**
40764      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40765      * @param {Mixed} value The value to set
40766      */
40767     setRawValue : function(v){
40768         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40769     },
40770
40771     /**
40772      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40773      * @param {Mixed} value The value to set
40774      */
40775     setValue : function(v){
40776         this.value = v;
40777         if(this.rendered){
40778             this.el.dom.value = (v === null || v === undefined ? '' : v);
40779              this.validate();
40780         }
40781     },
40782
40783     adjustSize : function(w, h){
40784         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40785         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40786         return s;
40787     },
40788
40789     adjustWidth : function(tag, w){
40790         tag = tag.toLowerCase();
40791         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40792             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40793                 if(tag == 'input'){
40794                     return w + 2;
40795                 }
40796                 if(tag == 'textarea'){
40797                     return w-2;
40798                 }
40799             }else if(Roo.isOpera){
40800                 if(tag == 'input'){
40801                     return w + 2;
40802                 }
40803                 if(tag == 'textarea'){
40804                     return w-2;
40805                 }
40806             }
40807         }
40808         return w;
40809     }
40810 });
40811
40812
40813 // anything other than normal should be considered experimental
40814 Roo.form.Field.msgFx = {
40815     normal : {
40816         show: function(msgEl, f){
40817             msgEl.setDisplayed('block');
40818         },
40819
40820         hide : function(msgEl, f){
40821             msgEl.setDisplayed(false).update('');
40822         }
40823     },
40824
40825     slide : {
40826         show: function(msgEl, f){
40827             msgEl.slideIn('t', {stopFx:true});
40828         },
40829
40830         hide : function(msgEl, f){
40831             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40832         }
40833     },
40834
40835     slideRight : {
40836         show: function(msgEl, f){
40837             msgEl.fixDisplay();
40838             msgEl.alignTo(f.el, 'tl-tr');
40839             msgEl.slideIn('l', {stopFx:true});
40840         },
40841
40842         hide : function(msgEl, f){
40843             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40844         }
40845     }
40846 };/*
40847  * Based on:
40848  * Ext JS Library 1.1.1
40849  * Copyright(c) 2006-2007, Ext JS, LLC.
40850  *
40851  * Originally Released Under LGPL - original licence link has changed is not relivant.
40852  *
40853  * Fork - LGPL
40854  * <script type="text/javascript">
40855  */
40856  
40857
40858 /**
40859  * @class Roo.form.TextField
40860  * @extends Roo.form.Field
40861  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40862  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40863  * @constructor
40864  * Creates a new TextField
40865  * @param {Object} config Configuration options
40866  */
40867 Roo.form.TextField = function(config){
40868     Roo.form.TextField.superclass.constructor.call(this, config);
40869     this.addEvents({
40870         /**
40871          * @event autosize
40872          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40873          * according to the default logic, but this event provides a hook for the developer to apply additional
40874          * logic at runtime to resize the field if needed.
40875              * @param {Roo.form.Field} this This text field
40876              * @param {Number} width The new field width
40877              */
40878         autosize : true
40879     });
40880 };
40881
40882 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40883     /**
40884      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40885      */
40886     grow : false,
40887     /**
40888      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40889      */
40890     growMin : 30,
40891     /**
40892      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40893      */
40894     growMax : 800,
40895     /**
40896      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40897      */
40898     vtype : null,
40899     /**
40900      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40901      */
40902     maskRe : null,
40903     /**
40904      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40905      */
40906     disableKeyFilter : false,
40907     /**
40908      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40909      */
40910     allowBlank : true,
40911     /**
40912      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40913      */
40914     minLength : 0,
40915     /**
40916      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40917      */
40918     maxLength : Number.MAX_VALUE,
40919     /**
40920      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40921      */
40922     minLengthText : "The minimum length for this field is {0}",
40923     /**
40924      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40925      */
40926     maxLengthText : "The maximum length for this field is {0}",
40927     /**
40928      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40929      */
40930     selectOnFocus : false,
40931     /**
40932      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40933      */    
40934     allowLeadingSpace : false,
40935     /**
40936      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40937      */
40938     blankText : "This field is required",
40939     /**
40940      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40941      * If available, this function will be called only after the basic validators all return true, and will be passed the
40942      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40943      */
40944     validator : null,
40945     /**
40946      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40947      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40948      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40949      */
40950     regex : null,
40951     /**
40952      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40953      */
40954     regexText : "",
40955     /**
40956      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40957      */
40958     emptyText : null,
40959    
40960
40961     // private
40962     initEvents : function()
40963     {
40964         if (this.emptyText) {
40965             this.el.attr('placeholder', this.emptyText);
40966         }
40967         
40968         Roo.form.TextField.superclass.initEvents.call(this);
40969         if(this.validationEvent == 'keyup'){
40970             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40971             this.el.on('keyup', this.filterValidation, this);
40972         }
40973         else if(this.validationEvent !== false){
40974             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40975         }
40976         
40977         if(this.selectOnFocus){
40978             this.on("focus", this.preFocus, this);
40979         }
40980         if (!this.allowLeadingSpace) {
40981             this.on('blur', this.cleanLeadingSpace, this);
40982         }
40983         
40984         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40985             this.el.on("keypress", this.filterKeys, this);
40986         }
40987         if(this.grow){
40988             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40989             this.el.on("click", this.autoSize,  this);
40990         }
40991         if(this.el.is('input[type=password]') && Roo.isSafari){
40992             this.el.on('keydown', this.SafariOnKeyDown, this);
40993         }
40994     },
40995
40996     processValue : function(value){
40997         if(this.stripCharsRe){
40998             var newValue = value.replace(this.stripCharsRe, '');
40999             if(newValue !== value){
41000                 this.setRawValue(newValue);
41001                 return newValue;
41002             }
41003         }
41004         return value;
41005     },
41006
41007     filterValidation : function(e){
41008         if(!e.isNavKeyPress()){
41009             this.validationTask.delay(this.validationDelay);
41010         }
41011     },
41012
41013     // private
41014     onKeyUp : function(e){
41015         if(!e.isNavKeyPress()){
41016             this.autoSize();
41017         }
41018     },
41019     // private - clean the leading white space
41020     cleanLeadingSpace : function(e)
41021     {
41022         if ( this.inputType == 'file') {
41023             return;
41024         }
41025         
41026         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41027     },
41028     /**
41029      * Resets the current field value to the originally-loaded value and clears any validation messages.
41030      *  
41031      */
41032     reset : function(){
41033         Roo.form.TextField.superclass.reset.call(this);
41034        
41035     }, 
41036     // private
41037     preFocus : function(){
41038         
41039         if(this.selectOnFocus){
41040             this.el.dom.select();
41041         }
41042     },
41043
41044     
41045     // private
41046     filterKeys : function(e){
41047         var k = e.getKey();
41048         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41049             return;
41050         }
41051         var c = e.getCharCode(), cc = String.fromCharCode(c);
41052         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41053             return;
41054         }
41055         if(!this.maskRe.test(cc)){
41056             e.stopEvent();
41057         }
41058     },
41059
41060     setValue : function(v){
41061         
41062         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41063         
41064         this.autoSize();
41065     },
41066
41067     /**
41068      * Validates a value according to the field's validation rules and marks the field as invalid
41069      * if the validation fails
41070      * @param {Mixed} value The value to validate
41071      * @return {Boolean} True if the value is valid, else false
41072      */
41073     validateValue : function(value){
41074         if(value.length < 1)  { // if it's blank
41075              if(this.allowBlank){
41076                 this.clearInvalid();
41077                 return true;
41078              }else{
41079                 this.markInvalid(this.blankText);
41080                 return false;
41081              }
41082         }
41083         if(value.length < this.minLength){
41084             this.markInvalid(String.format(this.minLengthText, this.minLength));
41085             return false;
41086         }
41087         if(value.length > this.maxLength){
41088             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41089             return false;
41090         }
41091         if(this.vtype){
41092             var vt = Roo.form.VTypes;
41093             if(!vt[this.vtype](value, this)){
41094                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41095                 return false;
41096             }
41097         }
41098         if(typeof this.validator == "function"){
41099             var msg = this.validator(value);
41100             if(msg !== true){
41101                 this.markInvalid(msg);
41102                 return false;
41103             }
41104         }
41105         if(this.regex && !this.regex.test(value)){
41106             this.markInvalid(this.regexText);
41107             return false;
41108         }
41109         return true;
41110     },
41111
41112     /**
41113      * Selects text in this field
41114      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41115      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41116      */
41117     selectText : function(start, end){
41118         var v = this.getRawValue();
41119         if(v.length > 0){
41120             start = start === undefined ? 0 : start;
41121             end = end === undefined ? v.length : end;
41122             var d = this.el.dom;
41123             if(d.setSelectionRange){
41124                 d.setSelectionRange(start, end);
41125             }else if(d.createTextRange){
41126                 var range = d.createTextRange();
41127                 range.moveStart("character", start);
41128                 range.moveEnd("character", v.length-end);
41129                 range.select();
41130             }
41131         }
41132     },
41133
41134     /**
41135      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41136      * This only takes effect if grow = true, and fires the autosize event.
41137      */
41138     autoSize : function(){
41139         if(!this.grow || !this.rendered){
41140             return;
41141         }
41142         if(!this.metrics){
41143             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41144         }
41145         var el = this.el;
41146         var v = el.dom.value;
41147         var d = document.createElement('div');
41148         d.appendChild(document.createTextNode(v));
41149         v = d.innerHTML;
41150         d = null;
41151         v += "&#160;";
41152         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41153         this.el.setWidth(w);
41154         this.fireEvent("autosize", this, w);
41155     },
41156     
41157     // private
41158     SafariOnKeyDown : function(event)
41159     {
41160         // this is a workaround for a password hang bug on chrome/ webkit.
41161         
41162         var isSelectAll = false;
41163         
41164         if(this.el.dom.selectionEnd > 0){
41165             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41166         }
41167         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41168             event.preventDefault();
41169             this.setValue('');
41170             return;
41171         }
41172         
41173         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41174             
41175             event.preventDefault();
41176             // this is very hacky as keydown always get's upper case.
41177             
41178             var cc = String.fromCharCode(event.getCharCode());
41179             
41180             
41181             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41182             
41183         }
41184         
41185         
41186     }
41187 });/*
41188  * Based on:
41189  * Ext JS Library 1.1.1
41190  * Copyright(c) 2006-2007, Ext JS, LLC.
41191  *
41192  * Originally Released Under LGPL - original licence link has changed is not relivant.
41193  *
41194  * Fork - LGPL
41195  * <script type="text/javascript">
41196  */
41197  
41198 /**
41199  * @class Roo.form.Hidden
41200  * @extends Roo.form.TextField
41201  * Simple Hidden element used on forms 
41202  * 
41203  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41204  * 
41205  * @constructor
41206  * Creates a new Hidden form element.
41207  * @param {Object} config Configuration options
41208  */
41209
41210
41211
41212 // easy hidden field...
41213 Roo.form.Hidden = function(config){
41214     Roo.form.Hidden.superclass.constructor.call(this, config);
41215 };
41216   
41217 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41218     fieldLabel:      '',
41219     inputType:      'hidden',
41220     width:          50,
41221     allowBlank:     true,
41222     labelSeparator: '',
41223     hidden:         true,
41224     itemCls :       'x-form-item-display-none'
41225
41226
41227 });
41228
41229
41230 /*
41231  * Based on:
41232  * Ext JS Library 1.1.1
41233  * Copyright(c) 2006-2007, Ext JS, LLC.
41234  *
41235  * Originally Released Under LGPL - original licence link has changed is not relivant.
41236  *
41237  * Fork - LGPL
41238  * <script type="text/javascript">
41239  */
41240  
41241 /**
41242  * @class Roo.form.TriggerField
41243  * @extends Roo.form.TextField
41244  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41245  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41246  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41247  * for which you can provide a custom implementation.  For example:
41248  * <pre><code>
41249 var trigger = new Roo.form.TriggerField();
41250 trigger.onTriggerClick = myTriggerFn;
41251 trigger.applyTo('my-field');
41252 </code></pre>
41253  *
41254  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41255  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41256  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41257  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41258  * @constructor
41259  * Create a new TriggerField.
41260  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41261  * to the base TextField)
41262  */
41263 Roo.form.TriggerField = function(config){
41264     this.mimicing = false;
41265     Roo.form.TriggerField.superclass.constructor.call(this, config);
41266 };
41267
41268 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41269     /**
41270      * @cfg {String} triggerClass A CSS class to apply to the trigger
41271      */
41272     /**
41273      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41274      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41275      */
41276     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41277     /**
41278      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41279      */
41280     hideTrigger:false,
41281
41282     /** @cfg {Boolean} grow @hide */
41283     /** @cfg {Number} growMin @hide */
41284     /** @cfg {Number} growMax @hide */
41285
41286     /**
41287      * @hide 
41288      * @method
41289      */
41290     autoSize: Roo.emptyFn,
41291     // private
41292     monitorTab : true,
41293     // private
41294     deferHeight : true,
41295
41296     
41297     actionMode : 'wrap',
41298     // private
41299     onResize : function(w, h){
41300         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41301         if(typeof w == 'number'){
41302             var x = w - this.trigger.getWidth();
41303             this.el.setWidth(this.adjustWidth('input', x));
41304             this.trigger.setStyle('left', x+'px');
41305         }
41306     },
41307
41308     // private
41309     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41310
41311     // private
41312     getResizeEl : function(){
41313         return this.wrap;
41314     },
41315
41316     // private
41317     getPositionEl : function(){
41318         return this.wrap;
41319     },
41320
41321     // private
41322     alignErrorIcon : function(){
41323         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41324     },
41325
41326     // private
41327     onRender : function(ct, position){
41328         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41329         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41330         this.trigger = this.wrap.createChild(this.triggerConfig ||
41331                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41332         if(this.hideTrigger){
41333             this.trigger.setDisplayed(false);
41334         }
41335         this.initTrigger();
41336         if(!this.width){
41337             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41338         }
41339     },
41340
41341     // private
41342     initTrigger : function(){
41343         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41344         this.trigger.addClassOnOver('x-form-trigger-over');
41345         this.trigger.addClassOnClick('x-form-trigger-click');
41346     },
41347
41348     // private
41349     onDestroy : function(){
41350         if(this.trigger){
41351             this.trigger.removeAllListeners();
41352             this.trigger.remove();
41353         }
41354         if(this.wrap){
41355             this.wrap.remove();
41356         }
41357         Roo.form.TriggerField.superclass.onDestroy.call(this);
41358     },
41359
41360     // private
41361     onFocus : function(){
41362         Roo.form.TriggerField.superclass.onFocus.call(this);
41363         if(!this.mimicing){
41364             this.wrap.addClass('x-trigger-wrap-focus');
41365             this.mimicing = true;
41366             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41367             if(this.monitorTab){
41368                 this.el.on("keydown", this.checkTab, this);
41369             }
41370         }
41371     },
41372
41373     // private
41374     checkTab : function(e){
41375         if(e.getKey() == e.TAB){
41376             this.triggerBlur();
41377         }
41378     },
41379
41380     // private
41381     onBlur : function(){
41382         // do nothing
41383     },
41384
41385     // private
41386     mimicBlur : function(e, t){
41387         if(!this.wrap.contains(t) && this.validateBlur()){
41388             this.triggerBlur();
41389         }
41390     },
41391
41392     // private
41393     triggerBlur : function(){
41394         this.mimicing = false;
41395         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41396         if(this.monitorTab){
41397             this.el.un("keydown", this.checkTab, this);
41398         }
41399         this.wrap.removeClass('x-trigger-wrap-focus');
41400         Roo.form.TriggerField.superclass.onBlur.call(this);
41401     },
41402
41403     // private
41404     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41405     validateBlur : function(e, t){
41406         return true;
41407     },
41408
41409     // private
41410     onDisable : function(){
41411         Roo.form.TriggerField.superclass.onDisable.call(this);
41412         if(this.wrap){
41413             this.wrap.addClass('x-item-disabled');
41414         }
41415     },
41416
41417     // private
41418     onEnable : function(){
41419         Roo.form.TriggerField.superclass.onEnable.call(this);
41420         if(this.wrap){
41421             this.wrap.removeClass('x-item-disabled');
41422         }
41423     },
41424
41425     // private
41426     onShow : function(){
41427         var ae = this.getActionEl();
41428         
41429         if(ae){
41430             ae.dom.style.display = '';
41431             ae.dom.style.visibility = 'visible';
41432         }
41433     },
41434
41435     // private
41436     
41437     onHide : function(){
41438         var ae = this.getActionEl();
41439         ae.dom.style.display = 'none';
41440     },
41441
41442     /**
41443      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41444      * by an implementing function.
41445      * @method
41446      * @param {EventObject} e
41447      */
41448     onTriggerClick : Roo.emptyFn
41449 });
41450
41451 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41452 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41453 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41454 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41455     initComponent : function(){
41456         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41457
41458         this.triggerConfig = {
41459             tag:'span', cls:'x-form-twin-triggers', cn:[
41460             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41461             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41462         ]};
41463     },
41464
41465     getTrigger : function(index){
41466         return this.triggers[index];
41467     },
41468
41469     initTrigger : function(){
41470         var ts = this.trigger.select('.x-form-trigger', true);
41471         this.wrap.setStyle('overflow', 'hidden');
41472         var triggerField = this;
41473         ts.each(function(t, all, index){
41474             t.hide = function(){
41475                 var w = triggerField.wrap.getWidth();
41476                 this.dom.style.display = 'none';
41477                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41478             };
41479             t.show = function(){
41480                 var w = triggerField.wrap.getWidth();
41481                 this.dom.style.display = '';
41482                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41483             };
41484             var triggerIndex = 'Trigger'+(index+1);
41485
41486             if(this['hide'+triggerIndex]){
41487                 t.dom.style.display = 'none';
41488             }
41489             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41490             t.addClassOnOver('x-form-trigger-over');
41491             t.addClassOnClick('x-form-trigger-click');
41492         }, this);
41493         this.triggers = ts.elements;
41494     },
41495
41496     onTrigger1Click : Roo.emptyFn,
41497     onTrigger2Click : Roo.emptyFn
41498 });/*
41499  * Based on:
41500  * Ext JS Library 1.1.1
41501  * Copyright(c) 2006-2007, Ext JS, LLC.
41502  *
41503  * Originally Released Under LGPL - original licence link has changed is not relivant.
41504  *
41505  * Fork - LGPL
41506  * <script type="text/javascript">
41507  */
41508  
41509 /**
41510  * @class Roo.form.TextArea
41511  * @extends Roo.form.TextField
41512  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41513  * support for auto-sizing.
41514  * @constructor
41515  * Creates a new TextArea
41516  * @param {Object} config Configuration options
41517  */
41518 Roo.form.TextArea = function(config){
41519     Roo.form.TextArea.superclass.constructor.call(this, config);
41520     // these are provided exchanges for backwards compat
41521     // minHeight/maxHeight were replaced by growMin/growMax to be
41522     // compatible with TextField growing config values
41523     if(this.minHeight !== undefined){
41524         this.growMin = this.minHeight;
41525     }
41526     if(this.maxHeight !== undefined){
41527         this.growMax = this.maxHeight;
41528     }
41529 };
41530
41531 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41532     /**
41533      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41534      */
41535     growMin : 60,
41536     /**
41537      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41538      */
41539     growMax: 1000,
41540     /**
41541      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41542      * in the field (equivalent to setting overflow: hidden, defaults to false)
41543      */
41544     preventScrollbars: false,
41545     /**
41546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41547      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41548      */
41549
41550     // private
41551     onRender : function(ct, position){
41552         if(!this.el){
41553             this.defaultAutoCreate = {
41554                 tag: "textarea",
41555                 style:"width:300px;height:60px;",
41556                 autocomplete: "new-password"
41557             };
41558         }
41559         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41560         if(this.grow){
41561             this.textSizeEl = Roo.DomHelper.append(document.body, {
41562                 tag: "pre", cls: "x-form-grow-sizer"
41563             });
41564             if(this.preventScrollbars){
41565                 this.el.setStyle("overflow", "hidden");
41566             }
41567             this.el.setHeight(this.growMin);
41568         }
41569     },
41570
41571     onDestroy : function(){
41572         if(this.textSizeEl){
41573             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41574         }
41575         Roo.form.TextArea.superclass.onDestroy.call(this);
41576     },
41577
41578     // private
41579     onKeyUp : function(e){
41580         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41581             this.autoSize();
41582         }
41583     },
41584
41585     /**
41586      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41587      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41588      */
41589     autoSize : function(){
41590         if(!this.grow || !this.textSizeEl){
41591             return;
41592         }
41593         var el = this.el;
41594         var v = el.dom.value;
41595         var ts = this.textSizeEl;
41596
41597         ts.innerHTML = '';
41598         ts.appendChild(document.createTextNode(v));
41599         v = ts.innerHTML;
41600
41601         Roo.fly(ts).setWidth(this.el.getWidth());
41602         if(v.length < 1){
41603             v = "&#160;&#160;";
41604         }else{
41605             if(Roo.isIE){
41606                 v = v.replace(/\n/g, '<p>&#160;</p>');
41607             }
41608             v += "&#160;\n&#160;";
41609         }
41610         ts.innerHTML = v;
41611         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41612         if(h != this.lastHeight){
41613             this.lastHeight = h;
41614             this.el.setHeight(h);
41615             this.fireEvent("autosize", this, h);
41616         }
41617     }
41618 });/*
41619  * Based on:
41620  * Ext JS Library 1.1.1
41621  * Copyright(c) 2006-2007, Ext JS, LLC.
41622  *
41623  * Originally Released Under LGPL - original licence link has changed is not relivant.
41624  *
41625  * Fork - LGPL
41626  * <script type="text/javascript">
41627  */
41628  
41629
41630 /**
41631  * @class Roo.form.NumberField
41632  * @extends Roo.form.TextField
41633  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41634  * @constructor
41635  * Creates a new NumberField
41636  * @param {Object} config Configuration options
41637  */
41638 Roo.form.NumberField = function(config){
41639     Roo.form.NumberField.superclass.constructor.call(this, config);
41640 };
41641
41642 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41643     /**
41644      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41645      */
41646     fieldClass: "x-form-field x-form-num-field",
41647     /**
41648      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41649      */
41650     allowDecimals : true,
41651     /**
41652      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41653      */
41654     decimalSeparator : ".",
41655     /**
41656      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41657      */
41658     decimalPrecision : 2,
41659     /**
41660      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41661      */
41662     allowNegative : true,
41663     /**
41664      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41665      */
41666     minValue : Number.NEGATIVE_INFINITY,
41667     /**
41668      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41669      */
41670     maxValue : Number.MAX_VALUE,
41671     /**
41672      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41673      */
41674     minText : "The minimum value for this field is {0}",
41675     /**
41676      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41677      */
41678     maxText : "The maximum value for this field is {0}",
41679     /**
41680      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41681      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41682      */
41683     nanText : "{0} is not a valid number",
41684
41685     // private
41686     initEvents : function(){
41687         Roo.form.NumberField.superclass.initEvents.call(this);
41688         var allowed = "0123456789";
41689         if(this.allowDecimals){
41690             allowed += this.decimalSeparator;
41691         }
41692         if(this.allowNegative){
41693             allowed += "-";
41694         }
41695         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41696         var keyPress = function(e){
41697             var k = e.getKey();
41698             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41699                 return;
41700             }
41701             var c = e.getCharCode();
41702             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41703                 e.stopEvent();
41704             }
41705         };
41706         this.el.on("keypress", keyPress, this);
41707     },
41708
41709     // private
41710     validateValue : function(value){
41711         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41712             return false;
41713         }
41714         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41715              return true;
41716         }
41717         var num = this.parseValue(value);
41718         if(isNaN(num)){
41719             this.markInvalid(String.format(this.nanText, value));
41720             return false;
41721         }
41722         if(num < this.minValue){
41723             this.markInvalid(String.format(this.minText, this.minValue));
41724             return false;
41725         }
41726         if(num > this.maxValue){
41727             this.markInvalid(String.format(this.maxText, this.maxValue));
41728             return false;
41729         }
41730         return true;
41731     },
41732
41733     getValue : function(){
41734         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41735     },
41736
41737     // private
41738     parseValue : function(value){
41739         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41740         return isNaN(value) ? '' : value;
41741     },
41742
41743     // private
41744     fixPrecision : function(value){
41745         var nan = isNaN(value);
41746         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41747             return nan ? '' : value;
41748         }
41749         return parseFloat(value).toFixed(this.decimalPrecision);
41750     },
41751
41752     setValue : function(v){
41753         v = this.fixPrecision(v);
41754         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41755     },
41756
41757     // private
41758     decimalPrecisionFcn : function(v){
41759         return Math.floor(v);
41760     },
41761
41762     beforeBlur : function(){
41763         var v = this.parseValue(this.getRawValue());
41764         if(v){
41765             this.setValue(v);
41766         }
41767     }
41768 });/*
41769  * Based on:
41770  * Ext JS Library 1.1.1
41771  * Copyright(c) 2006-2007, Ext JS, LLC.
41772  *
41773  * Originally Released Under LGPL - original licence link has changed is not relivant.
41774  *
41775  * Fork - LGPL
41776  * <script type="text/javascript">
41777  */
41778  
41779 /**
41780  * @class Roo.form.DateField
41781  * @extends Roo.form.TriggerField
41782  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41783 * @constructor
41784 * Create a new DateField
41785 * @param {Object} config
41786  */
41787 Roo.form.DateField = function(config)
41788 {
41789     Roo.form.DateField.superclass.constructor.call(this, config);
41790     
41791       this.addEvents({
41792          
41793         /**
41794          * @event select
41795          * Fires when a date is selected
41796              * @param {Roo.form.DateField} combo This combo box
41797              * @param {Date} date The date selected
41798              */
41799         'select' : true
41800          
41801     });
41802     
41803     
41804     if(typeof this.minValue == "string") {
41805         this.minValue = this.parseDate(this.minValue);
41806     }
41807     if(typeof this.maxValue == "string") {
41808         this.maxValue = this.parseDate(this.maxValue);
41809     }
41810     this.ddMatch = null;
41811     if(this.disabledDates){
41812         var dd = this.disabledDates;
41813         var re = "(?:";
41814         for(var i = 0; i < dd.length; i++){
41815             re += dd[i];
41816             if(i != dd.length-1) {
41817                 re += "|";
41818             }
41819         }
41820         this.ddMatch = new RegExp(re + ")");
41821     }
41822 };
41823
41824 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41825     /**
41826      * @cfg {String} format
41827      * The default date format string which can be overriden for localization support.  The format must be
41828      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41829      */
41830     format : "m/d/y",
41831     /**
41832      * @cfg {String} altFormats
41833      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41834      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41835      */
41836     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41837     /**
41838      * @cfg {Array} disabledDays
41839      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41840      */
41841     disabledDays : null,
41842     /**
41843      * @cfg {String} disabledDaysText
41844      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41845      */
41846     disabledDaysText : "Disabled",
41847     /**
41848      * @cfg {Array} disabledDates
41849      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41850      * expression so they are very powerful. Some examples:
41851      * <ul>
41852      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41853      * <li>["03/08", "09/16"] would disable those days for every year</li>
41854      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41855      * <li>["03/../2006"] would disable every day in March 2006</li>
41856      * <li>["^03"] would disable every day in every March</li>
41857      * </ul>
41858      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41859      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41860      */
41861     disabledDates : null,
41862     /**
41863      * @cfg {String} disabledDatesText
41864      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41865      */
41866     disabledDatesText : "Disabled",
41867         
41868         
41869         /**
41870      * @cfg {Date/String} zeroValue
41871      * if the date is less that this number, then the field is rendered as empty
41872      * default is 1800
41873      */
41874         zeroValue : '1800-01-01',
41875         
41876         
41877     /**
41878      * @cfg {Date/String} minValue
41879      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41880      * valid format (defaults to null).
41881      */
41882     minValue : null,
41883     /**
41884      * @cfg {Date/String} maxValue
41885      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41886      * valid format (defaults to null).
41887      */
41888     maxValue : null,
41889     /**
41890      * @cfg {String} minText
41891      * The error text to display when the date in the cell is before minValue (defaults to
41892      * 'The date in this field must be after {minValue}').
41893      */
41894     minText : "The date in this field must be equal to or after {0}",
41895     /**
41896      * @cfg {String} maxText
41897      * The error text to display when the date in the cell is after maxValue (defaults to
41898      * 'The date in this field must be before {maxValue}').
41899      */
41900     maxText : "The date in this field must be equal to or before {0}",
41901     /**
41902      * @cfg {String} invalidText
41903      * The error text to display when the date in the field is invalid (defaults to
41904      * '{value} is not a valid date - it must be in the format {format}').
41905      */
41906     invalidText : "{0} is not a valid date - it must be in the format {1}",
41907     /**
41908      * @cfg {String} triggerClass
41909      * An additional CSS class used to style the trigger button.  The trigger will always get the
41910      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41911      * which displays a calendar icon).
41912      */
41913     triggerClass : 'x-form-date-trigger',
41914     
41915
41916     /**
41917      * @cfg {Boolean} useIso
41918      * if enabled, then the date field will use a hidden field to store the 
41919      * real value as iso formated date. default (false)
41920      */ 
41921     useIso : false,
41922     /**
41923      * @cfg {String/Object} autoCreate
41924      * A DomHelper element spec, or true for a default element spec (defaults to
41925      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41926      */ 
41927     // private
41928     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41929     
41930     // private
41931     hiddenField: false,
41932     
41933     onRender : function(ct, position)
41934     {
41935         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41936         if (this.useIso) {
41937             //this.el.dom.removeAttribute('name'); 
41938             Roo.log("Changing name?");
41939             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41940             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41941                     'before', true);
41942             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41943             // prevent input submission
41944             this.hiddenName = this.name;
41945         }
41946             
41947             
41948     },
41949     
41950     // private
41951     validateValue : function(value)
41952     {
41953         value = this.formatDate(value);
41954         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41955             Roo.log('super failed');
41956             return false;
41957         }
41958         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41959              return true;
41960         }
41961         var svalue = value;
41962         value = this.parseDate(value);
41963         if(!value){
41964             Roo.log('parse date failed' + svalue);
41965             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41966             return false;
41967         }
41968         var time = value.getTime();
41969         if(this.minValue && time < this.minValue.getTime()){
41970             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41971             return false;
41972         }
41973         if(this.maxValue && time > this.maxValue.getTime()){
41974             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41975             return false;
41976         }
41977         if(this.disabledDays){
41978             var day = value.getDay();
41979             for(var i = 0; i < this.disabledDays.length; i++) {
41980                 if(day === this.disabledDays[i]){
41981                     this.markInvalid(this.disabledDaysText);
41982                     return false;
41983                 }
41984             }
41985         }
41986         var fvalue = this.formatDate(value);
41987         if(this.ddMatch && this.ddMatch.test(fvalue)){
41988             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41989             return false;
41990         }
41991         return true;
41992     },
41993
41994     // private
41995     // Provides logic to override the default TriggerField.validateBlur which just returns true
41996     validateBlur : function(){
41997         return !this.menu || !this.menu.isVisible();
41998     },
41999     
42000     getName: function()
42001     {
42002         // returns hidden if it's set..
42003         if (!this.rendered) {return ''};
42004         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42005         
42006     },
42007
42008     /**
42009      * Returns the current date value of the date field.
42010      * @return {Date} The date value
42011      */
42012     getValue : function(){
42013         
42014         return  this.hiddenField ?
42015                 this.hiddenField.value :
42016                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42017     },
42018
42019     /**
42020      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42021      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42022      * (the default format used is "m/d/y").
42023      * <br />Usage:
42024      * <pre><code>
42025 //All of these calls set the same date value (May 4, 2006)
42026
42027 //Pass a date object:
42028 var dt = new Date('5/4/06');
42029 dateField.setValue(dt);
42030
42031 //Pass a date string (default format):
42032 dateField.setValue('5/4/06');
42033
42034 //Pass a date string (custom format):
42035 dateField.format = 'Y-m-d';
42036 dateField.setValue('2006-5-4');
42037 </code></pre>
42038      * @param {String/Date} date The date or valid date string
42039      */
42040     setValue : function(date){
42041         if (this.hiddenField) {
42042             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42043         }
42044         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42045         // make sure the value field is always stored as a date..
42046         this.value = this.parseDate(date);
42047         
42048         
42049     },
42050
42051     // private
42052     parseDate : function(value){
42053                 
42054                 if (value instanceof Date) {
42055                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42056                                 return  '';
42057                         }
42058                         return value;
42059                 }
42060                 
42061                 
42062         if(!value || value instanceof Date){
42063             return value;
42064         }
42065         var v = Date.parseDate(value, this.format);
42066          if (!v && this.useIso) {
42067             v = Date.parseDate(value, 'Y-m-d');
42068         }
42069         if(!v && this.altFormats){
42070             if(!this.altFormatsArray){
42071                 this.altFormatsArray = this.altFormats.split("|");
42072             }
42073             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42074                 v = Date.parseDate(value, this.altFormatsArray[i]);
42075             }
42076         }
42077                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42078                         v = '';
42079                 }
42080         return v;
42081     },
42082
42083     // private
42084     formatDate : function(date, fmt){
42085         return (!date || !(date instanceof Date)) ?
42086                date : date.dateFormat(fmt || this.format);
42087     },
42088
42089     // private
42090     menuListeners : {
42091         select: function(m, d){
42092             
42093             this.setValue(d);
42094             this.fireEvent('select', this, d);
42095         },
42096         show : function(){ // retain focus styling
42097             this.onFocus();
42098         },
42099         hide : function(){
42100             this.focus.defer(10, this);
42101             var ml = this.menuListeners;
42102             this.menu.un("select", ml.select,  this);
42103             this.menu.un("show", ml.show,  this);
42104             this.menu.un("hide", ml.hide,  this);
42105         }
42106     },
42107
42108     // private
42109     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42110     onTriggerClick : function(){
42111         if(this.disabled){
42112             return;
42113         }
42114         if(this.menu == null){
42115             this.menu = new Roo.menu.DateMenu();
42116         }
42117         Roo.apply(this.menu.picker,  {
42118             showClear: this.allowBlank,
42119             minDate : this.minValue,
42120             maxDate : this.maxValue,
42121             disabledDatesRE : this.ddMatch,
42122             disabledDatesText : this.disabledDatesText,
42123             disabledDays : this.disabledDays,
42124             disabledDaysText : this.disabledDaysText,
42125             format : this.useIso ? 'Y-m-d' : this.format,
42126             minText : String.format(this.minText, this.formatDate(this.minValue)),
42127             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42128         });
42129         this.menu.on(Roo.apply({}, this.menuListeners, {
42130             scope:this
42131         }));
42132         this.menu.picker.setValue(this.getValue() || new Date());
42133         this.menu.show(this.el, "tl-bl?");
42134     },
42135
42136     beforeBlur : function(){
42137         var v = this.parseDate(this.getRawValue());
42138         if(v){
42139             this.setValue(v);
42140         }
42141     },
42142
42143     /*@
42144      * overide
42145      * 
42146      */
42147     isDirty : function() {
42148         if(this.disabled) {
42149             return false;
42150         }
42151         
42152         if(typeof(this.startValue) === 'undefined'){
42153             return false;
42154         }
42155         
42156         return String(this.getValue()) !== String(this.startValue);
42157         
42158     },
42159     // @overide
42160     cleanLeadingSpace : function(e)
42161     {
42162        return;
42163     }
42164     
42165 });/*
42166  * Based on:
42167  * Ext JS Library 1.1.1
42168  * Copyright(c) 2006-2007, Ext JS, LLC.
42169  *
42170  * Originally Released Under LGPL - original licence link has changed is not relivant.
42171  *
42172  * Fork - LGPL
42173  * <script type="text/javascript">
42174  */
42175  
42176 /**
42177  * @class Roo.form.MonthField
42178  * @extends Roo.form.TriggerField
42179  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42180 * @constructor
42181 * Create a new MonthField
42182 * @param {Object} config
42183  */
42184 Roo.form.MonthField = function(config){
42185     
42186     Roo.form.MonthField.superclass.constructor.call(this, config);
42187     
42188       this.addEvents({
42189          
42190         /**
42191          * @event select
42192          * Fires when a date is selected
42193              * @param {Roo.form.MonthFieeld} combo This combo box
42194              * @param {Date} date The date selected
42195              */
42196         'select' : true
42197          
42198     });
42199     
42200     
42201     if(typeof this.minValue == "string") {
42202         this.minValue = this.parseDate(this.minValue);
42203     }
42204     if(typeof this.maxValue == "string") {
42205         this.maxValue = this.parseDate(this.maxValue);
42206     }
42207     this.ddMatch = null;
42208     if(this.disabledDates){
42209         var dd = this.disabledDates;
42210         var re = "(?:";
42211         for(var i = 0; i < dd.length; i++){
42212             re += dd[i];
42213             if(i != dd.length-1) {
42214                 re += "|";
42215             }
42216         }
42217         this.ddMatch = new RegExp(re + ")");
42218     }
42219 };
42220
42221 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42222     /**
42223      * @cfg {String} format
42224      * The default date format string which can be overriden for localization support.  The format must be
42225      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42226      */
42227     format : "M Y",
42228     /**
42229      * @cfg {String} altFormats
42230      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42231      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42232      */
42233     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42234     /**
42235      * @cfg {Array} disabledDays
42236      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42237      */
42238     disabledDays : [0,1,2,3,4,5,6],
42239     /**
42240      * @cfg {String} disabledDaysText
42241      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42242      */
42243     disabledDaysText : "Disabled",
42244     /**
42245      * @cfg {Array} disabledDates
42246      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42247      * expression so they are very powerful. Some examples:
42248      * <ul>
42249      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42250      * <li>["03/08", "09/16"] would disable those days for every year</li>
42251      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42252      * <li>["03/../2006"] would disable every day in March 2006</li>
42253      * <li>["^03"] would disable every day in every March</li>
42254      * </ul>
42255      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42256      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42257      */
42258     disabledDates : null,
42259     /**
42260      * @cfg {String} disabledDatesText
42261      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42262      */
42263     disabledDatesText : "Disabled",
42264     /**
42265      * @cfg {Date/String} minValue
42266      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42267      * valid format (defaults to null).
42268      */
42269     minValue : null,
42270     /**
42271      * @cfg {Date/String} maxValue
42272      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42273      * valid format (defaults to null).
42274      */
42275     maxValue : null,
42276     /**
42277      * @cfg {String} minText
42278      * The error text to display when the date in the cell is before minValue (defaults to
42279      * 'The date in this field must be after {minValue}').
42280      */
42281     minText : "The date in this field must be equal to or after {0}",
42282     /**
42283      * @cfg {String} maxTextf
42284      * The error text to display when the date in the cell is after maxValue (defaults to
42285      * 'The date in this field must be before {maxValue}').
42286      */
42287     maxText : "The date in this field must be equal to or before {0}",
42288     /**
42289      * @cfg {String} invalidText
42290      * The error text to display when the date in the field is invalid (defaults to
42291      * '{value} is not a valid date - it must be in the format {format}').
42292      */
42293     invalidText : "{0} is not a valid date - it must be in the format {1}",
42294     /**
42295      * @cfg {String} triggerClass
42296      * An additional CSS class used to style the trigger button.  The trigger will always get the
42297      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42298      * which displays a calendar icon).
42299      */
42300     triggerClass : 'x-form-date-trigger',
42301     
42302
42303     /**
42304      * @cfg {Boolean} useIso
42305      * if enabled, then the date field will use a hidden field to store the 
42306      * real value as iso formated date. default (true)
42307      */ 
42308     useIso : true,
42309     /**
42310      * @cfg {String/Object} autoCreate
42311      * A DomHelper element spec, or true for a default element spec (defaults to
42312      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42313      */ 
42314     // private
42315     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42316     
42317     // private
42318     hiddenField: false,
42319     
42320     hideMonthPicker : false,
42321     
42322     onRender : function(ct, position)
42323     {
42324         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42325         if (this.useIso) {
42326             this.el.dom.removeAttribute('name'); 
42327             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42328                     'before', true);
42329             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42330             // prevent input submission
42331             this.hiddenName = this.name;
42332         }
42333             
42334             
42335     },
42336     
42337     // private
42338     validateValue : function(value)
42339     {
42340         value = this.formatDate(value);
42341         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42342             return false;
42343         }
42344         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42345              return true;
42346         }
42347         var svalue = value;
42348         value = this.parseDate(value);
42349         if(!value){
42350             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42351             return false;
42352         }
42353         var time = value.getTime();
42354         if(this.minValue && time < this.minValue.getTime()){
42355             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42356             return false;
42357         }
42358         if(this.maxValue && time > this.maxValue.getTime()){
42359             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42360             return false;
42361         }
42362         /*if(this.disabledDays){
42363             var day = value.getDay();
42364             for(var i = 0; i < this.disabledDays.length; i++) {
42365                 if(day === this.disabledDays[i]){
42366                     this.markInvalid(this.disabledDaysText);
42367                     return false;
42368                 }
42369             }
42370         }
42371         */
42372         var fvalue = this.formatDate(value);
42373         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42374             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42375             return false;
42376         }
42377         */
42378         return true;
42379     },
42380
42381     // private
42382     // Provides logic to override the default TriggerField.validateBlur which just returns true
42383     validateBlur : function(){
42384         return !this.menu || !this.menu.isVisible();
42385     },
42386
42387     /**
42388      * Returns the current date value of the date field.
42389      * @return {Date} The date value
42390      */
42391     getValue : function(){
42392         
42393         
42394         
42395         return  this.hiddenField ?
42396                 this.hiddenField.value :
42397                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42398     },
42399
42400     /**
42401      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42402      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42403      * (the default format used is "m/d/y").
42404      * <br />Usage:
42405      * <pre><code>
42406 //All of these calls set the same date value (May 4, 2006)
42407
42408 //Pass a date object:
42409 var dt = new Date('5/4/06');
42410 monthField.setValue(dt);
42411
42412 //Pass a date string (default format):
42413 monthField.setValue('5/4/06');
42414
42415 //Pass a date string (custom format):
42416 monthField.format = 'Y-m-d';
42417 monthField.setValue('2006-5-4');
42418 </code></pre>
42419      * @param {String/Date} date The date or valid date string
42420      */
42421     setValue : function(date){
42422         Roo.log('month setValue' + date);
42423         // can only be first of month..
42424         
42425         var val = this.parseDate(date);
42426         
42427         if (this.hiddenField) {
42428             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42429         }
42430         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42431         this.value = this.parseDate(date);
42432     },
42433
42434     // private
42435     parseDate : function(value){
42436         if(!value || value instanceof Date){
42437             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42438             return value;
42439         }
42440         var v = Date.parseDate(value, this.format);
42441         if (!v && this.useIso) {
42442             v = Date.parseDate(value, 'Y-m-d');
42443         }
42444         if (v) {
42445             // 
42446             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42447         }
42448         
42449         
42450         if(!v && this.altFormats){
42451             if(!this.altFormatsArray){
42452                 this.altFormatsArray = this.altFormats.split("|");
42453             }
42454             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42455                 v = Date.parseDate(value, this.altFormatsArray[i]);
42456             }
42457         }
42458         return v;
42459     },
42460
42461     // private
42462     formatDate : function(date, fmt){
42463         return (!date || !(date instanceof Date)) ?
42464                date : date.dateFormat(fmt || this.format);
42465     },
42466
42467     // private
42468     menuListeners : {
42469         select: function(m, d){
42470             this.setValue(d);
42471             this.fireEvent('select', this, d);
42472         },
42473         show : function(){ // retain focus styling
42474             this.onFocus();
42475         },
42476         hide : function(){
42477             this.focus.defer(10, this);
42478             var ml = this.menuListeners;
42479             this.menu.un("select", ml.select,  this);
42480             this.menu.un("show", ml.show,  this);
42481             this.menu.un("hide", ml.hide,  this);
42482         }
42483     },
42484     // private
42485     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42486     onTriggerClick : function(){
42487         if(this.disabled){
42488             return;
42489         }
42490         if(this.menu == null){
42491             this.menu = new Roo.menu.DateMenu();
42492            
42493         }
42494         
42495         Roo.apply(this.menu.picker,  {
42496             
42497             showClear: this.allowBlank,
42498             minDate : this.minValue,
42499             maxDate : this.maxValue,
42500             disabledDatesRE : this.ddMatch,
42501             disabledDatesText : this.disabledDatesText,
42502             
42503             format : this.useIso ? 'Y-m-d' : this.format,
42504             minText : String.format(this.minText, this.formatDate(this.minValue)),
42505             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42506             
42507         });
42508          this.menu.on(Roo.apply({}, this.menuListeners, {
42509             scope:this
42510         }));
42511        
42512         
42513         var m = this.menu;
42514         var p = m.picker;
42515         
42516         // hide month picker get's called when we called by 'before hide';
42517         
42518         var ignorehide = true;
42519         p.hideMonthPicker  = function(disableAnim){
42520             if (ignorehide) {
42521                 return;
42522             }
42523              if(this.monthPicker){
42524                 Roo.log("hideMonthPicker called");
42525                 if(disableAnim === true){
42526                     this.monthPicker.hide();
42527                 }else{
42528                     this.monthPicker.slideOut('t', {duration:.2});
42529                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42530                     p.fireEvent("select", this, this.value);
42531                     m.hide();
42532                 }
42533             }
42534         }
42535         
42536         Roo.log('picker set value');
42537         Roo.log(this.getValue());
42538         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42539         m.show(this.el, 'tl-bl?');
42540         ignorehide  = false;
42541         // this will trigger hideMonthPicker..
42542         
42543         
42544         // hidden the day picker
42545         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42546         
42547         
42548         
42549       
42550         
42551         p.showMonthPicker.defer(100, p);
42552     
42553         
42554        
42555     },
42556
42557     beforeBlur : function(){
42558         var v = this.parseDate(this.getRawValue());
42559         if(v){
42560             this.setValue(v);
42561         }
42562     }
42563
42564     /** @cfg {Boolean} grow @hide */
42565     /** @cfg {Number} growMin @hide */
42566     /** @cfg {Number} growMax @hide */
42567     /**
42568      * @hide
42569      * @method autoSize
42570      */
42571 });/*
42572  * Based on:
42573  * Ext JS Library 1.1.1
42574  * Copyright(c) 2006-2007, Ext JS, LLC.
42575  *
42576  * Originally Released Under LGPL - original licence link has changed is not relivant.
42577  *
42578  * Fork - LGPL
42579  * <script type="text/javascript">
42580  */
42581  
42582
42583 /**
42584  * @class Roo.form.ComboBox
42585  * @extends Roo.form.TriggerField
42586  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42587  * @constructor
42588  * Create a new ComboBox.
42589  * @param {Object} config Configuration options
42590  */
42591 Roo.form.ComboBox = function(config){
42592     Roo.form.ComboBox.superclass.constructor.call(this, config);
42593     this.addEvents({
42594         /**
42595          * @event expand
42596          * Fires when the dropdown list is expanded
42597              * @param {Roo.form.ComboBox} combo This combo box
42598              */
42599         'expand' : true,
42600         /**
42601          * @event collapse
42602          * Fires when the dropdown list is collapsed
42603              * @param {Roo.form.ComboBox} combo This combo box
42604              */
42605         'collapse' : true,
42606         /**
42607          * @event beforeselect
42608          * Fires before a list item is selected. Return false to cancel the selection.
42609              * @param {Roo.form.ComboBox} combo This combo box
42610              * @param {Roo.data.Record} record The data record returned from the underlying store
42611              * @param {Number} index The index of the selected item in the dropdown list
42612              */
42613         'beforeselect' : true,
42614         /**
42615          * @event select
42616          * Fires when a list item is selected
42617              * @param {Roo.form.ComboBox} combo This combo box
42618              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42619              * @param {Number} index The index of the selected item in the dropdown list
42620              */
42621         'select' : true,
42622         /**
42623          * @event beforequery
42624          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42625          * The event object passed has these properties:
42626              * @param {Roo.form.ComboBox} combo This combo box
42627              * @param {String} query The query
42628              * @param {Boolean} forceAll true to force "all" query
42629              * @param {Boolean} cancel true to cancel the query
42630              * @param {Object} e The query event object
42631              */
42632         'beforequery': true,
42633          /**
42634          * @event add
42635          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42636              * @param {Roo.form.ComboBox} combo This combo box
42637              */
42638         'add' : true,
42639         /**
42640          * @event edit
42641          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42642              * @param {Roo.form.ComboBox} combo This combo box
42643              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42644              */
42645         'edit' : true
42646         
42647         
42648     });
42649     if(this.transform){
42650         this.allowDomMove = false;
42651         var s = Roo.getDom(this.transform);
42652         if(!this.hiddenName){
42653             this.hiddenName = s.name;
42654         }
42655         if(!this.store){
42656             this.mode = 'local';
42657             var d = [], opts = s.options;
42658             for(var i = 0, len = opts.length;i < len; i++){
42659                 var o = opts[i];
42660                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42661                 if(o.selected) {
42662                     this.value = value;
42663                 }
42664                 d.push([value, o.text]);
42665             }
42666             this.store = new Roo.data.SimpleStore({
42667                 'id': 0,
42668                 fields: ['value', 'text'],
42669                 data : d
42670             });
42671             this.valueField = 'value';
42672             this.displayField = 'text';
42673         }
42674         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42675         if(!this.lazyRender){
42676             this.target = true;
42677             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42678             s.parentNode.removeChild(s); // remove it
42679             this.render(this.el.parentNode);
42680         }else{
42681             s.parentNode.removeChild(s); // remove it
42682         }
42683
42684     }
42685     if (this.store) {
42686         this.store = Roo.factory(this.store, Roo.data);
42687     }
42688     
42689     this.selectedIndex = -1;
42690     if(this.mode == 'local'){
42691         if(config.queryDelay === undefined){
42692             this.queryDelay = 10;
42693         }
42694         if(config.minChars === undefined){
42695             this.minChars = 0;
42696         }
42697     }
42698 };
42699
42700 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42701     /**
42702      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42703      */
42704     /**
42705      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42706      * rendering into an Roo.Editor, defaults to false)
42707      */
42708     /**
42709      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42710      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42711      */
42712     /**
42713      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42714      */
42715     /**
42716      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42717      * the dropdown list (defaults to undefined, with no header element)
42718      */
42719
42720      /**
42721      * @cfg {String/Roo.Template} tpl The template to use to render the output
42722      */
42723      
42724     // private
42725     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42726     /**
42727      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42728      */
42729     listWidth: undefined,
42730     /**
42731      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42732      * mode = 'remote' or 'text' if mode = 'local')
42733      */
42734     displayField: undefined,
42735     /**
42736      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42737      * mode = 'remote' or 'value' if mode = 'local'). 
42738      * Note: use of a valueField requires the user make a selection
42739      * in order for a value to be mapped.
42740      */
42741     valueField: undefined,
42742     
42743     
42744     /**
42745      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42746      * field's data value (defaults to the underlying DOM element's name)
42747      */
42748     hiddenName: undefined,
42749     /**
42750      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42751      */
42752     listClass: '',
42753     /**
42754      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42755      */
42756     selectedClass: 'x-combo-selected',
42757     /**
42758      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42759      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42760      * which displays a downward arrow icon).
42761      */
42762     triggerClass : 'x-form-arrow-trigger',
42763     /**
42764      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42765      */
42766     shadow:'sides',
42767     /**
42768      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42769      * anchor positions (defaults to 'tl-bl')
42770      */
42771     listAlign: 'tl-bl?',
42772     /**
42773      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42774      */
42775     maxHeight: 300,
42776     /**
42777      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42778      * query specified by the allQuery config option (defaults to 'query')
42779      */
42780     triggerAction: 'query',
42781     /**
42782      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42783      * (defaults to 4, does not apply if editable = false)
42784      */
42785     minChars : 4,
42786     /**
42787      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42788      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42789      */
42790     typeAhead: false,
42791     /**
42792      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42793      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42794      */
42795     queryDelay: 500,
42796     /**
42797      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42798      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42799      */
42800     pageSize: 0,
42801     /**
42802      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42803      * when editable = true (defaults to false)
42804      */
42805     selectOnFocus:false,
42806     /**
42807      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42808      */
42809     queryParam: 'query',
42810     /**
42811      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42812      * when mode = 'remote' (defaults to 'Loading...')
42813      */
42814     loadingText: 'Loading...',
42815     /**
42816      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42817      */
42818     resizable: false,
42819     /**
42820      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42821      */
42822     handleHeight : 8,
42823     /**
42824      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42825      * traditional select (defaults to true)
42826      */
42827     editable: true,
42828     /**
42829      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42830      */
42831     allQuery: '',
42832     /**
42833      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42834      */
42835     mode: 'remote',
42836     /**
42837      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42838      * listWidth has a higher value)
42839      */
42840     minListWidth : 70,
42841     /**
42842      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42843      * allow the user to set arbitrary text into the field (defaults to false)
42844      */
42845     forceSelection:false,
42846     /**
42847      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42848      * if typeAhead = true (defaults to 250)
42849      */
42850     typeAheadDelay : 250,
42851     /**
42852      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42853      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42854      */
42855     valueNotFoundText : undefined,
42856     /**
42857      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42858      */
42859     blockFocus : false,
42860     
42861     /**
42862      * @cfg {Boolean} disableClear Disable showing of clear button.
42863      */
42864     disableClear : false,
42865     /**
42866      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42867      */
42868     alwaysQuery : false,
42869     
42870     //private
42871     addicon : false,
42872     editicon: false,
42873     
42874     // element that contains real text value.. (when hidden is used..)
42875      
42876     // private
42877     onRender : function(ct, position)
42878     {
42879         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42880         
42881         if(this.hiddenName){
42882             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42883                     'before', true);
42884             this.hiddenField.value =
42885                 this.hiddenValue !== undefined ? this.hiddenValue :
42886                 this.value !== undefined ? this.value : '';
42887
42888             // prevent input submission
42889             this.el.dom.removeAttribute('name');
42890              
42891              
42892         }
42893         
42894         if(Roo.isGecko){
42895             this.el.dom.setAttribute('autocomplete', 'off');
42896         }
42897
42898         var cls = 'x-combo-list';
42899
42900         this.list = new Roo.Layer({
42901             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42902         });
42903
42904         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42905         this.list.setWidth(lw);
42906         this.list.swallowEvent('mousewheel');
42907         this.assetHeight = 0;
42908
42909         if(this.title){
42910             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42911             this.assetHeight += this.header.getHeight();
42912         }
42913
42914         this.innerList = this.list.createChild({cls:cls+'-inner'});
42915         this.innerList.on('mouseover', this.onViewOver, this);
42916         this.innerList.on('mousemove', this.onViewMove, this);
42917         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42918         
42919         if(this.allowBlank && !this.pageSize && !this.disableClear){
42920             this.footer = this.list.createChild({cls:cls+'-ft'});
42921             this.pageTb = new Roo.Toolbar(this.footer);
42922            
42923         }
42924         if(this.pageSize){
42925             this.footer = this.list.createChild({cls:cls+'-ft'});
42926             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42927                     {pageSize: this.pageSize});
42928             
42929         }
42930         
42931         if (this.pageTb && this.allowBlank && !this.disableClear) {
42932             var _this = this;
42933             this.pageTb.add(new Roo.Toolbar.Fill(), {
42934                 cls: 'x-btn-icon x-btn-clear',
42935                 text: '&#160;',
42936                 handler: function()
42937                 {
42938                     _this.collapse();
42939                     _this.clearValue();
42940                     _this.onSelect(false, -1);
42941                 }
42942             });
42943         }
42944         if (this.footer) {
42945             this.assetHeight += this.footer.getHeight();
42946         }
42947         
42948
42949         if(!this.tpl){
42950             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42951         }
42952
42953         this.view = new Roo.View(this.innerList, this.tpl, {
42954             singleSelect:true,
42955             store: this.store,
42956             selectedClass: this.selectedClass
42957         });
42958
42959         this.view.on('click', this.onViewClick, this);
42960
42961         this.store.on('beforeload', this.onBeforeLoad, this);
42962         this.store.on('load', this.onLoad, this);
42963         this.store.on('loadexception', this.onLoadException, this);
42964
42965         if(this.resizable){
42966             this.resizer = new Roo.Resizable(this.list,  {
42967                pinned:true, handles:'se'
42968             });
42969             this.resizer.on('resize', function(r, w, h){
42970                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42971                 this.listWidth = w;
42972                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42973                 this.restrictHeight();
42974             }, this);
42975             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42976         }
42977         if(!this.editable){
42978             this.editable = true;
42979             this.setEditable(false);
42980         }  
42981         
42982         
42983         if (typeof(this.events.add.listeners) != 'undefined') {
42984             
42985             this.addicon = this.wrap.createChild(
42986                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42987        
42988             this.addicon.on('click', function(e) {
42989                 this.fireEvent('add', this);
42990             }, this);
42991         }
42992         if (typeof(this.events.edit.listeners) != 'undefined') {
42993             
42994             this.editicon = this.wrap.createChild(
42995                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42996             if (this.addicon) {
42997                 this.editicon.setStyle('margin-left', '40px');
42998             }
42999             this.editicon.on('click', function(e) {
43000                 
43001                 // we fire even  if inothing is selected..
43002                 this.fireEvent('edit', this, this.lastData );
43003                 
43004             }, this);
43005         }
43006         
43007         
43008         
43009     },
43010
43011     // private
43012     initEvents : function(){
43013         Roo.form.ComboBox.superclass.initEvents.call(this);
43014
43015         this.keyNav = new Roo.KeyNav(this.el, {
43016             "up" : function(e){
43017                 this.inKeyMode = true;
43018                 this.selectPrev();
43019             },
43020
43021             "down" : function(e){
43022                 if(!this.isExpanded()){
43023                     this.onTriggerClick();
43024                 }else{
43025                     this.inKeyMode = true;
43026                     this.selectNext();
43027                 }
43028             },
43029
43030             "enter" : function(e){
43031                 this.onViewClick();
43032                 //return true;
43033             },
43034
43035             "esc" : function(e){
43036                 this.collapse();
43037             },
43038
43039             "tab" : function(e){
43040                 this.onViewClick(false);
43041                 this.fireEvent("specialkey", this, e);
43042                 return true;
43043             },
43044
43045             scope : this,
43046
43047             doRelay : function(foo, bar, hname){
43048                 if(hname == 'down' || this.scope.isExpanded()){
43049                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43050                 }
43051                 return true;
43052             },
43053
43054             forceKeyDown: true
43055         });
43056         this.queryDelay = Math.max(this.queryDelay || 10,
43057                 this.mode == 'local' ? 10 : 250);
43058         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43059         if(this.typeAhead){
43060             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43061         }
43062         if(this.editable !== false){
43063             this.el.on("keyup", this.onKeyUp, this);
43064         }
43065         if(this.forceSelection){
43066             this.on('blur', this.doForce, this);
43067         }
43068     },
43069
43070     onDestroy : function(){
43071         if(this.view){
43072             this.view.setStore(null);
43073             this.view.el.removeAllListeners();
43074             this.view.el.remove();
43075             this.view.purgeListeners();
43076         }
43077         if(this.list){
43078             this.list.destroy();
43079         }
43080         if(this.store){
43081             this.store.un('beforeload', this.onBeforeLoad, this);
43082             this.store.un('load', this.onLoad, this);
43083             this.store.un('loadexception', this.onLoadException, this);
43084         }
43085         Roo.form.ComboBox.superclass.onDestroy.call(this);
43086     },
43087
43088     // private
43089     fireKey : function(e){
43090         if(e.isNavKeyPress() && !this.list.isVisible()){
43091             this.fireEvent("specialkey", this, e);
43092         }
43093     },
43094
43095     // private
43096     onResize: function(w, h){
43097         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43098         
43099         if(typeof w != 'number'){
43100             // we do not handle it!?!?
43101             return;
43102         }
43103         var tw = this.trigger.getWidth();
43104         tw += this.addicon ? this.addicon.getWidth() : 0;
43105         tw += this.editicon ? this.editicon.getWidth() : 0;
43106         var x = w - tw;
43107         this.el.setWidth( this.adjustWidth('input', x));
43108             
43109         this.trigger.setStyle('left', x+'px');
43110         
43111         if(this.list && this.listWidth === undefined){
43112             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43113             this.list.setWidth(lw);
43114             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43115         }
43116         
43117     
43118         
43119     },
43120
43121     /**
43122      * Allow or prevent the user from directly editing the field text.  If false is passed,
43123      * the user will only be able to select from the items defined in the dropdown list.  This method
43124      * is the runtime equivalent of setting the 'editable' config option at config time.
43125      * @param {Boolean} value True to allow the user to directly edit the field text
43126      */
43127     setEditable : function(value){
43128         if(value == this.editable){
43129             return;
43130         }
43131         this.editable = value;
43132         if(!value){
43133             this.el.dom.setAttribute('readOnly', true);
43134             this.el.on('mousedown', this.onTriggerClick,  this);
43135             this.el.addClass('x-combo-noedit');
43136         }else{
43137             this.el.dom.setAttribute('readOnly', false);
43138             this.el.un('mousedown', this.onTriggerClick,  this);
43139             this.el.removeClass('x-combo-noedit');
43140         }
43141     },
43142
43143     // private
43144     onBeforeLoad : function(){
43145         if(!this.hasFocus){
43146             return;
43147         }
43148         this.innerList.update(this.loadingText ?
43149                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43150         this.restrictHeight();
43151         this.selectedIndex = -1;
43152     },
43153
43154     // private
43155     onLoad : function(){
43156         if(!this.hasFocus){
43157             return;
43158         }
43159         if(this.store.getCount() > 0){
43160             this.expand();
43161             this.restrictHeight();
43162             if(this.lastQuery == this.allQuery){
43163                 if(this.editable){
43164                     this.el.dom.select();
43165                 }
43166                 if(!this.selectByValue(this.value, true)){
43167                     this.select(0, true);
43168                 }
43169             }else{
43170                 this.selectNext();
43171                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43172                     this.taTask.delay(this.typeAheadDelay);
43173                 }
43174             }
43175         }else{
43176             this.onEmptyResults();
43177         }
43178         //this.el.focus();
43179     },
43180     // private
43181     onLoadException : function()
43182     {
43183         this.collapse();
43184         Roo.log(this.store.reader.jsonData);
43185         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43186             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43187         }
43188         
43189         
43190     },
43191     // private
43192     onTypeAhead : function(){
43193         if(this.store.getCount() > 0){
43194             var r = this.store.getAt(0);
43195             var newValue = r.data[this.displayField];
43196             var len = newValue.length;
43197             var selStart = this.getRawValue().length;
43198             if(selStart != len){
43199                 this.setRawValue(newValue);
43200                 this.selectText(selStart, newValue.length);
43201             }
43202         }
43203     },
43204
43205     // private
43206     onSelect : function(record, index){
43207         if(this.fireEvent('beforeselect', this, record, index) !== false){
43208             this.setFromData(index > -1 ? record.data : false);
43209             this.collapse();
43210             this.fireEvent('select', this, record, index);
43211         }
43212     },
43213
43214     /**
43215      * Returns the currently selected field value or empty string if no value is set.
43216      * @return {String} value The selected value
43217      */
43218     getValue : function(){
43219         if(this.valueField){
43220             return typeof this.value != 'undefined' ? this.value : '';
43221         }
43222         return Roo.form.ComboBox.superclass.getValue.call(this);
43223     },
43224
43225     /**
43226      * Clears any text/value currently set in the field
43227      */
43228     clearValue : function(){
43229         if(this.hiddenField){
43230             this.hiddenField.value = '';
43231         }
43232         this.value = '';
43233         this.setRawValue('');
43234         this.lastSelectionText = '';
43235         
43236     },
43237
43238     /**
43239      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43240      * will be displayed in the field.  If the value does not match the data value of an existing item,
43241      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43242      * Otherwise the field will be blank (although the value will still be set).
43243      * @param {String} value The value to match
43244      */
43245     setValue : function(v){
43246         var text = v;
43247         if(this.valueField){
43248             var r = this.findRecord(this.valueField, v);
43249             if(r){
43250                 text = r.data[this.displayField];
43251             }else if(this.valueNotFoundText !== undefined){
43252                 text = this.valueNotFoundText;
43253             }
43254         }
43255         this.lastSelectionText = text;
43256         if(this.hiddenField){
43257             this.hiddenField.value = v;
43258         }
43259         Roo.form.ComboBox.superclass.setValue.call(this, text);
43260         this.value = v;
43261     },
43262     /**
43263      * @property {Object} the last set data for the element
43264      */
43265     
43266     lastData : false,
43267     /**
43268      * Sets the value of the field based on a object which is related to the record format for the store.
43269      * @param {Object} value the value to set as. or false on reset?
43270      */
43271     setFromData : function(o){
43272         var dv = ''; // display value
43273         var vv = ''; // value value..
43274         this.lastData = o;
43275         if (this.displayField) {
43276             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43277         } else {
43278             // this is an error condition!!!
43279             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43280         }
43281         
43282         if(this.valueField){
43283             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43284         }
43285         if(this.hiddenField){
43286             this.hiddenField.value = vv;
43287             
43288             this.lastSelectionText = dv;
43289             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43290             this.value = vv;
43291             return;
43292         }
43293         // no hidden field.. - we store the value in 'value', but still display
43294         // display field!!!!
43295         this.lastSelectionText = dv;
43296         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43297         this.value = vv;
43298         
43299         
43300     },
43301     // private
43302     reset : function(){
43303         // overridden so that last data is reset..
43304         this.setValue(this.resetValue);
43305         this.originalValue = this.getValue();
43306         this.clearInvalid();
43307         this.lastData = false;
43308         if (this.view) {
43309             this.view.clearSelections();
43310         }
43311     },
43312     // private
43313     findRecord : function(prop, value){
43314         var record;
43315         if(this.store.getCount() > 0){
43316             this.store.each(function(r){
43317                 if(r.data[prop] == value){
43318                     record = r;
43319                     return false;
43320                 }
43321                 return true;
43322             });
43323         }
43324         return record;
43325     },
43326     
43327     getName: function()
43328     {
43329         // returns hidden if it's set..
43330         if (!this.rendered) {return ''};
43331         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43332         
43333     },
43334     // private
43335     onViewMove : function(e, t){
43336         this.inKeyMode = false;
43337     },
43338
43339     // private
43340     onViewOver : function(e, t){
43341         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43342             return;
43343         }
43344         var item = this.view.findItemFromChild(t);
43345         if(item){
43346             var index = this.view.indexOf(item);
43347             this.select(index, false);
43348         }
43349     },
43350
43351     // private
43352     onViewClick : function(doFocus)
43353     {
43354         var index = this.view.getSelectedIndexes()[0];
43355         var r = this.store.getAt(index);
43356         if(r){
43357             this.onSelect(r, index);
43358         }
43359         if(doFocus !== false && !this.blockFocus){
43360             this.el.focus();
43361         }
43362     },
43363
43364     // private
43365     restrictHeight : function(){
43366         this.innerList.dom.style.height = '';
43367         var inner = this.innerList.dom;
43368         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43369         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43370         this.list.beginUpdate();
43371         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43372         this.list.alignTo(this.el, this.listAlign);
43373         this.list.endUpdate();
43374     },
43375
43376     // private
43377     onEmptyResults : function(){
43378         this.collapse();
43379     },
43380
43381     /**
43382      * Returns true if the dropdown list is expanded, else false.
43383      */
43384     isExpanded : function(){
43385         return this.list.isVisible();
43386     },
43387
43388     /**
43389      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43390      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43391      * @param {String} value The data value of the item to select
43392      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43393      * selected item if it is not currently in view (defaults to true)
43394      * @return {Boolean} True if the value matched an item in the list, else false
43395      */
43396     selectByValue : function(v, scrollIntoView){
43397         if(v !== undefined && v !== null){
43398             var r = this.findRecord(this.valueField || this.displayField, v);
43399             if(r){
43400                 this.select(this.store.indexOf(r), scrollIntoView);
43401                 return true;
43402             }
43403         }
43404         return false;
43405     },
43406
43407     /**
43408      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43409      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43410      * @param {Number} index The zero-based index of the list item to select
43411      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43412      * selected item if it is not currently in view (defaults to true)
43413      */
43414     select : function(index, scrollIntoView){
43415         this.selectedIndex = index;
43416         this.view.select(index);
43417         if(scrollIntoView !== false){
43418             var el = this.view.getNode(index);
43419             if(el){
43420                 this.innerList.scrollChildIntoView(el, false);
43421             }
43422         }
43423     },
43424
43425     // private
43426     selectNext : function(){
43427         var ct = this.store.getCount();
43428         if(ct > 0){
43429             if(this.selectedIndex == -1){
43430                 this.select(0);
43431             }else if(this.selectedIndex < ct-1){
43432                 this.select(this.selectedIndex+1);
43433             }
43434         }
43435     },
43436
43437     // private
43438     selectPrev : function(){
43439         var ct = this.store.getCount();
43440         if(ct > 0){
43441             if(this.selectedIndex == -1){
43442                 this.select(0);
43443             }else if(this.selectedIndex != 0){
43444                 this.select(this.selectedIndex-1);
43445             }
43446         }
43447     },
43448
43449     // private
43450     onKeyUp : function(e){
43451         if(this.editable !== false && !e.isSpecialKey()){
43452             this.lastKey = e.getKey();
43453             this.dqTask.delay(this.queryDelay);
43454         }
43455     },
43456
43457     // private
43458     validateBlur : function(){
43459         return !this.list || !this.list.isVisible();   
43460     },
43461
43462     // private
43463     initQuery : function(){
43464         this.doQuery(this.getRawValue());
43465     },
43466
43467     // private
43468     doForce : function(){
43469         if(this.el.dom.value.length > 0){
43470             this.el.dom.value =
43471                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43472              
43473         }
43474     },
43475
43476     /**
43477      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43478      * query allowing the query action to be canceled if needed.
43479      * @param {String} query The SQL query to execute
43480      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43481      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43482      * saved in the current store (defaults to false)
43483      */
43484     doQuery : function(q, forceAll){
43485         if(q === undefined || q === null){
43486             q = '';
43487         }
43488         var qe = {
43489             query: q,
43490             forceAll: forceAll,
43491             combo: this,
43492             cancel:false
43493         };
43494         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43495             return false;
43496         }
43497         q = qe.query;
43498         forceAll = qe.forceAll;
43499         if(forceAll === true || (q.length >= this.minChars)){
43500             if(this.lastQuery != q || this.alwaysQuery){
43501                 this.lastQuery = q;
43502                 if(this.mode == 'local'){
43503                     this.selectedIndex = -1;
43504                     if(forceAll){
43505                         this.store.clearFilter();
43506                     }else{
43507                         this.store.filter(this.displayField, q);
43508                     }
43509                     this.onLoad();
43510                 }else{
43511                     this.store.baseParams[this.queryParam] = q;
43512                     this.store.load({
43513                         params: this.getParams(q)
43514                     });
43515                     this.expand();
43516                 }
43517             }else{
43518                 this.selectedIndex = -1;
43519                 this.onLoad();   
43520             }
43521         }
43522     },
43523
43524     // private
43525     getParams : function(q){
43526         var p = {};
43527         //p[this.queryParam] = q;
43528         if(this.pageSize){
43529             p.start = 0;
43530             p.limit = this.pageSize;
43531         }
43532         return p;
43533     },
43534
43535     /**
43536      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43537      */
43538     collapse : function(){
43539         if(!this.isExpanded()){
43540             return;
43541         }
43542         this.list.hide();
43543         Roo.get(document).un('mousedown', this.collapseIf, this);
43544         Roo.get(document).un('mousewheel', this.collapseIf, this);
43545         if (!this.editable) {
43546             Roo.get(document).un('keydown', this.listKeyPress, this);
43547         }
43548         this.fireEvent('collapse', this);
43549     },
43550
43551     // private
43552     collapseIf : function(e){
43553         if(!e.within(this.wrap) && !e.within(this.list)){
43554             this.collapse();
43555         }
43556     },
43557
43558     /**
43559      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43560      */
43561     expand : function(){
43562         if(this.isExpanded() || !this.hasFocus){
43563             return;
43564         }
43565         this.list.alignTo(this.el, this.listAlign);
43566         this.list.show();
43567         Roo.get(document).on('mousedown', this.collapseIf, this);
43568         Roo.get(document).on('mousewheel', this.collapseIf, this);
43569         if (!this.editable) {
43570             Roo.get(document).on('keydown', this.listKeyPress, this);
43571         }
43572         
43573         this.fireEvent('expand', this);
43574     },
43575
43576     // private
43577     // Implements the default empty TriggerField.onTriggerClick function
43578     onTriggerClick : function(){
43579         if(this.disabled){
43580             return;
43581         }
43582         if(this.isExpanded()){
43583             this.collapse();
43584             if (!this.blockFocus) {
43585                 this.el.focus();
43586             }
43587             
43588         }else {
43589             this.hasFocus = true;
43590             if(this.triggerAction == 'all') {
43591                 this.doQuery(this.allQuery, true);
43592             } else {
43593                 this.doQuery(this.getRawValue());
43594             }
43595             if (!this.blockFocus) {
43596                 this.el.focus();
43597             }
43598         }
43599     },
43600     listKeyPress : function(e)
43601     {
43602         //Roo.log('listkeypress');
43603         // scroll to first matching element based on key pres..
43604         if (e.isSpecialKey()) {
43605             return false;
43606         }
43607         var k = String.fromCharCode(e.getKey()).toUpperCase();
43608         //Roo.log(k);
43609         var match  = false;
43610         var csel = this.view.getSelectedNodes();
43611         var cselitem = false;
43612         if (csel.length) {
43613             var ix = this.view.indexOf(csel[0]);
43614             cselitem  = this.store.getAt(ix);
43615             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43616                 cselitem = false;
43617             }
43618             
43619         }
43620         
43621         this.store.each(function(v) { 
43622             if (cselitem) {
43623                 // start at existing selection.
43624                 if (cselitem.id == v.id) {
43625                     cselitem = false;
43626                 }
43627                 return;
43628             }
43629                 
43630             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43631                 match = this.store.indexOf(v);
43632                 return false;
43633             }
43634         }, this);
43635         
43636         if (match === false) {
43637             return true; // no more action?
43638         }
43639         // scroll to?
43640         this.view.select(match);
43641         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43642         sn.scrollIntoView(sn.dom.parentNode, false);
43643     } 
43644
43645     /** 
43646     * @cfg {Boolean} grow 
43647     * @hide 
43648     */
43649     /** 
43650     * @cfg {Number} growMin 
43651     * @hide 
43652     */
43653     /** 
43654     * @cfg {Number} growMax 
43655     * @hide 
43656     */
43657     /**
43658      * @hide
43659      * @method autoSize
43660      */
43661 });/*
43662  * Copyright(c) 2010-2012, Roo J Solutions Limited
43663  *
43664  * Licence LGPL
43665  *
43666  */
43667
43668 /**
43669  * @class Roo.form.ComboBoxArray
43670  * @extends Roo.form.TextField
43671  * A facebook style adder... for lists of email / people / countries  etc...
43672  * pick multiple items from a combo box, and shows each one.
43673  *
43674  *  Fred [x]  Brian [x]  [Pick another |v]
43675  *
43676  *
43677  *  For this to work: it needs various extra information
43678  *    - normal combo problay has
43679  *      name, hiddenName
43680  *    + displayField, valueField
43681  *
43682  *    For our purpose...
43683  *
43684  *
43685  *   If we change from 'extends' to wrapping...
43686  *   
43687  *  
43688  *
43689  
43690  
43691  * @constructor
43692  * Create a new ComboBoxArray.
43693  * @param {Object} config Configuration options
43694  */
43695  
43696
43697 Roo.form.ComboBoxArray = function(config)
43698 {
43699     this.addEvents({
43700         /**
43701          * @event beforeremove
43702          * Fires before remove the value from the list
43703              * @param {Roo.form.ComboBoxArray} _self This combo box array
43704              * @param {Roo.form.ComboBoxArray.Item} item removed item
43705              */
43706         'beforeremove' : true,
43707         /**
43708          * @event remove
43709          * Fires when remove the value from the list
43710              * @param {Roo.form.ComboBoxArray} _self This combo box array
43711              * @param {Roo.form.ComboBoxArray.Item} item removed item
43712              */
43713         'remove' : true
43714         
43715         
43716     });
43717     
43718     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43719     
43720     this.items = new Roo.util.MixedCollection(false);
43721     
43722     // construct the child combo...
43723     
43724     
43725     
43726     
43727    
43728     
43729 }
43730
43731  
43732 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43733
43734     /**
43735      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43736      */
43737     
43738     lastData : false,
43739     
43740     // behavies liek a hiddne field
43741     inputType:      'hidden',
43742     /**
43743      * @cfg {Number} width The width of the box that displays the selected element
43744      */ 
43745     width:          300,
43746
43747     
43748     
43749     /**
43750      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43751      */
43752     name : false,
43753     /**
43754      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43755      */
43756     hiddenName : false,
43757       /**
43758      * @cfg {String} seperator    The value seperator normally ',' 
43759      */
43760     seperator : ',',
43761     
43762     // private the array of items that are displayed..
43763     items  : false,
43764     // private - the hidden field el.
43765     hiddenEl : false,
43766     // private - the filed el..
43767     el : false,
43768     
43769     //validateValue : function() { return true; }, // all values are ok!
43770     //onAddClick: function() { },
43771     
43772     onRender : function(ct, position) 
43773     {
43774         
43775         // create the standard hidden element
43776         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43777         
43778         
43779         // give fake names to child combo;
43780         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43781         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43782         
43783         this.combo = Roo.factory(this.combo, Roo.form);
43784         this.combo.onRender(ct, position);
43785         if (typeof(this.combo.width) != 'undefined') {
43786             this.combo.onResize(this.combo.width,0);
43787         }
43788         
43789         this.combo.initEvents();
43790         
43791         // assigned so form know we need to do this..
43792         this.store          = this.combo.store;
43793         this.valueField     = this.combo.valueField;
43794         this.displayField   = this.combo.displayField ;
43795         
43796         
43797         this.combo.wrap.addClass('x-cbarray-grp');
43798         
43799         var cbwrap = this.combo.wrap.createChild(
43800             {tag: 'div', cls: 'x-cbarray-cb'},
43801             this.combo.el.dom
43802         );
43803         
43804              
43805         this.hiddenEl = this.combo.wrap.createChild({
43806             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43807         });
43808         this.el = this.combo.wrap.createChild({
43809             tag: 'input',  type:'hidden' , name: this.name, value : ''
43810         });
43811          //   this.el.dom.removeAttribute("name");
43812         
43813         
43814         this.outerWrap = this.combo.wrap;
43815         this.wrap = cbwrap;
43816         
43817         this.outerWrap.setWidth(this.width);
43818         this.outerWrap.dom.removeChild(this.el.dom);
43819         
43820         this.wrap.dom.appendChild(this.el.dom);
43821         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43822         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43823         
43824         this.combo.trigger.setStyle('position','relative');
43825         this.combo.trigger.setStyle('left', '0px');
43826         this.combo.trigger.setStyle('top', '2px');
43827         
43828         this.combo.el.setStyle('vertical-align', 'text-bottom');
43829         
43830         //this.trigger.setStyle('vertical-align', 'top');
43831         
43832         // this should use the code from combo really... on('add' ....)
43833         if (this.adder) {
43834             
43835         
43836             this.adder = this.outerWrap.createChild(
43837                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43838             var _t = this;
43839             this.adder.on('click', function(e) {
43840                 _t.fireEvent('adderclick', this, e);
43841             }, _t);
43842         }
43843         //var _t = this;
43844         //this.adder.on('click', this.onAddClick, _t);
43845         
43846         
43847         this.combo.on('select', function(cb, rec, ix) {
43848             this.addItem(rec.data);
43849             
43850             cb.setValue('');
43851             cb.el.dom.value = '';
43852             //cb.lastData = rec.data;
43853             // add to list
43854             
43855         }, this);
43856         
43857         
43858     },
43859     
43860     
43861     getName: function()
43862     {
43863         // returns hidden if it's set..
43864         if (!this.rendered) {return ''};
43865         return  this.hiddenName ? this.hiddenName : this.name;
43866         
43867     },
43868     
43869     
43870     onResize: function(w, h){
43871         
43872         return;
43873         // not sure if this is needed..
43874         //this.combo.onResize(w,h);
43875         
43876         if(typeof w != 'number'){
43877             // we do not handle it!?!?
43878             return;
43879         }
43880         var tw = this.combo.trigger.getWidth();
43881         tw += this.addicon ? this.addicon.getWidth() : 0;
43882         tw += this.editicon ? this.editicon.getWidth() : 0;
43883         var x = w - tw;
43884         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43885             
43886         this.combo.trigger.setStyle('left', '0px');
43887         
43888         if(this.list && this.listWidth === undefined){
43889             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43890             this.list.setWidth(lw);
43891             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43892         }
43893         
43894     
43895         
43896     },
43897     
43898     addItem: function(rec)
43899     {
43900         var valueField = this.combo.valueField;
43901         var displayField = this.combo.displayField;
43902         
43903         if (this.items.indexOfKey(rec[valueField]) > -1) {
43904             //console.log("GOT " + rec.data.id);
43905             return;
43906         }
43907         
43908         var x = new Roo.form.ComboBoxArray.Item({
43909             //id : rec[this.idField],
43910             data : rec,
43911             displayField : displayField ,
43912             tipField : displayField ,
43913             cb : this
43914         });
43915         // use the 
43916         this.items.add(rec[valueField],x);
43917         // add it before the element..
43918         this.updateHiddenEl();
43919         x.render(this.outerWrap, this.wrap.dom);
43920         // add the image handler..
43921     },
43922     
43923     updateHiddenEl : function()
43924     {
43925         this.validate();
43926         if (!this.hiddenEl) {
43927             return;
43928         }
43929         var ar = [];
43930         var idField = this.combo.valueField;
43931         
43932         this.items.each(function(f) {
43933             ar.push(f.data[idField]);
43934         });
43935         this.hiddenEl.dom.value = ar.join(this.seperator);
43936         this.validate();
43937     },
43938     
43939     reset : function()
43940     {
43941         this.items.clear();
43942         
43943         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43944            el.remove();
43945         });
43946         
43947         this.el.dom.value = '';
43948         if (this.hiddenEl) {
43949             this.hiddenEl.dom.value = '';
43950         }
43951         
43952     },
43953     getValue: function()
43954     {
43955         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43956     },
43957     setValue: function(v) // not a valid action - must use addItems..
43958     {
43959         
43960         this.reset();
43961          
43962         if (this.store.isLocal && (typeof(v) == 'string')) {
43963             // then we can use the store to find the values..
43964             // comma seperated at present.. this needs to allow JSON based encoding..
43965             this.hiddenEl.value  = v;
43966             var v_ar = [];
43967             Roo.each(v.split(this.seperator), function(k) {
43968                 Roo.log("CHECK " + this.valueField + ',' + k);
43969                 var li = this.store.query(this.valueField, k);
43970                 if (!li.length) {
43971                     return;
43972                 }
43973                 var add = {};
43974                 add[this.valueField] = k;
43975                 add[this.displayField] = li.item(0).data[this.displayField];
43976                 
43977                 this.addItem(add);
43978             }, this) 
43979              
43980         }
43981         if (typeof(v) == 'object' ) {
43982             // then let's assume it's an array of objects..
43983             Roo.each(v, function(l) {
43984                 var add = l;
43985                 if (typeof(l) == 'string') {
43986                     add = {};
43987                     add[this.valueField] = l;
43988                     add[this.displayField] = l
43989                 }
43990                 this.addItem(add);
43991             }, this);
43992              
43993         }
43994         
43995         
43996     },
43997     setFromData: function(v)
43998     {
43999         // this recieves an object, if setValues is called.
44000         this.reset();
44001         this.el.dom.value = v[this.displayField];
44002         this.hiddenEl.dom.value = v[this.valueField];
44003         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44004             return;
44005         }
44006         var kv = v[this.valueField];
44007         var dv = v[this.displayField];
44008         kv = typeof(kv) != 'string' ? '' : kv;
44009         dv = typeof(dv) != 'string' ? '' : dv;
44010         
44011         
44012         var keys = kv.split(this.seperator);
44013         var display = dv.split(this.seperator);
44014         for (var i = 0 ; i < keys.length; i++) {
44015             add = {};
44016             add[this.valueField] = keys[i];
44017             add[this.displayField] = display[i];
44018             this.addItem(add);
44019         }
44020       
44021         
44022     },
44023     
44024     /**
44025      * Validates the combox array value
44026      * @return {Boolean} True if the value is valid, else false
44027      */
44028     validate : function(){
44029         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44030             this.clearInvalid();
44031             return true;
44032         }
44033         return false;
44034     },
44035     
44036     validateValue : function(value){
44037         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44038         
44039     },
44040     
44041     /*@
44042      * overide
44043      * 
44044      */
44045     isDirty : function() {
44046         if(this.disabled) {
44047             return false;
44048         }
44049         
44050         try {
44051             var d = Roo.decode(String(this.originalValue));
44052         } catch (e) {
44053             return String(this.getValue()) !== String(this.originalValue);
44054         }
44055         
44056         var originalValue = [];
44057         
44058         for (var i = 0; i < d.length; i++){
44059             originalValue.push(d[i][this.valueField]);
44060         }
44061         
44062         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44063         
44064     }
44065     
44066 });
44067
44068
44069
44070 /**
44071  * @class Roo.form.ComboBoxArray.Item
44072  * @extends Roo.BoxComponent
44073  * A selected item in the list
44074  *  Fred [x]  Brian [x]  [Pick another |v]
44075  * 
44076  * @constructor
44077  * Create a new item.
44078  * @param {Object} config Configuration options
44079  */
44080  
44081 Roo.form.ComboBoxArray.Item = function(config) {
44082     config.id = Roo.id();
44083     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44084 }
44085
44086 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44087     data : {},
44088     cb: false,
44089     displayField : false,
44090     tipField : false,
44091     
44092     
44093     defaultAutoCreate : {
44094         tag: 'div',
44095         cls: 'x-cbarray-item',
44096         cn : [ 
44097             { tag: 'div' },
44098             {
44099                 tag: 'img',
44100                 width:16,
44101                 height : 16,
44102                 src : Roo.BLANK_IMAGE_URL ,
44103                 align: 'center'
44104             }
44105         ]
44106         
44107     },
44108     
44109  
44110     onRender : function(ct, position)
44111     {
44112         Roo.form.Field.superclass.onRender.call(this, ct, position);
44113         
44114         if(!this.el){
44115             var cfg = this.getAutoCreate();
44116             this.el = ct.createChild(cfg, position);
44117         }
44118         
44119         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44120         
44121         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44122             this.cb.renderer(this.data) :
44123             String.format('{0}',this.data[this.displayField]);
44124         
44125             
44126         this.el.child('div').dom.setAttribute('qtip',
44127                         String.format('{0}',this.data[this.tipField])
44128         );
44129         
44130         this.el.child('img').on('click', this.remove, this);
44131         
44132     },
44133    
44134     remove : function()
44135     {
44136         if(this.cb.disabled){
44137             return;
44138         }
44139         
44140         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44141             this.cb.items.remove(this);
44142             this.el.child('img').un('click', this.remove, this);
44143             this.el.remove();
44144             this.cb.updateHiddenEl();
44145
44146             this.cb.fireEvent('remove', this.cb, this);
44147         }
44148         
44149     }
44150 });/*
44151  * RooJS Library 1.1.1
44152  * Copyright(c) 2008-2011  Alan Knowles
44153  *
44154  * License - LGPL
44155  */
44156  
44157
44158 /**
44159  * @class Roo.form.ComboNested
44160  * @extends Roo.form.ComboBox
44161  * A combobox for that allows selection of nested items in a list,
44162  * eg.
44163  *
44164  *  Book
44165  *    -> red
44166  *    -> green
44167  *  Table
44168  *    -> square
44169  *      ->red
44170  *      ->green
44171  *    -> rectangle
44172  *      ->green
44173  *      
44174  * 
44175  * @constructor
44176  * Create a new ComboNested
44177  * @param {Object} config Configuration options
44178  */
44179 Roo.form.ComboNested = function(config){
44180     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44181     // should verify some data...
44182     // like
44183     // hiddenName = required..
44184     // displayField = required
44185     // valudField == required
44186     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44187     var _t = this;
44188     Roo.each(req, function(e) {
44189         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44190             throw "Roo.form.ComboNested : missing value for: " + e;
44191         }
44192     });
44193      
44194     
44195 };
44196
44197 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44198    
44199     /*
44200      * @config {Number} max Number of columns to show
44201      */
44202     
44203     maxColumns : 3,
44204    
44205     list : null, // the outermost div..
44206     innerLists : null, // the
44207     views : null,
44208     stores : null,
44209     // private
44210     loadingChildren : false,
44211     
44212     onRender : function(ct, position)
44213     {
44214         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44215         
44216         if(this.hiddenName){
44217             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44218                     'before', true);
44219             this.hiddenField.value =
44220                 this.hiddenValue !== undefined ? this.hiddenValue :
44221                 this.value !== undefined ? this.value : '';
44222
44223             // prevent input submission
44224             this.el.dom.removeAttribute('name');
44225              
44226              
44227         }
44228         
44229         if(Roo.isGecko){
44230             this.el.dom.setAttribute('autocomplete', 'off');
44231         }
44232
44233         var cls = 'x-combo-list';
44234
44235         this.list = new Roo.Layer({
44236             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44237         });
44238
44239         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44240         this.list.setWidth(lw);
44241         this.list.swallowEvent('mousewheel');
44242         this.assetHeight = 0;
44243
44244         if(this.title){
44245             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44246             this.assetHeight += this.header.getHeight();
44247         }
44248         this.innerLists = [];
44249         this.views = [];
44250         this.stores = [];
44251         for (var i =0 ; i < this.maxColumns; i++) {
44252             this.onRenderList( cls, i);
44253         }
44254         
44255         // always needs footer, as we are going to have an 'OK' button.
44256         this.footer = this.list.createChild({cls:cls+'-ft'});
44257         this.pageTb = new Roo.Toolbar(this.footer);  
44258         var _this = this;
44259         this.pageTb.add(  {
44260             
44261             text: 'Done',
44262             handler: function()
44263             {
44264                 _this.collapse();
44265             }
44266         });
44267         
44268         if ( this.allowBlank && !this.disableClear) {
44269             
44270             this.pageTb.add(new Roo.Toolbar.Fill(), {
44271                 cls: 'x-btn-icon x-btn-clear',
44272                 text: '&#160;',
44273                 handler: function()
44274                 {
44275                     _this.collapse();
44276                     _this.clearValue();
44277                     _this.onSelect(false, -1);
44278                 }
44279             });
44280         }
44281         if (this.footer) {
44282             this.assetHeight += this.footer.getHeight();
44283         }
44284         
44285     },
44286     onRenderList : function (  cls, i)
44287     {
44288         
44289         var lw = Math.floor(
44290                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44291         );
44292         
44293         this.list.setWidth(lw); // default to '1'
44294
44295         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44296         //il.on('mouseover', this.onViewOver, this, { list:  i });
44297         //il.on('mousemove', this.onViewMove, this, { list:  i });
44298         il.setWidth(lw);
44299         il.setStyle({ 'overflow-x' : 'hidden'});
44300
44301         if(!this.tpl){
44302             this.tpl = new Roo.Template({
44303                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44304                 isEmpty: function (value, allValues) {
44305                     //Roo.log(value);
44306                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44307                     return dl ? 'has-children' : 'no-children'
44308                 }
44309             });
44310         }
44311         
44312         var store  = this.store;
44313         if (i > 0) {
44314             store  = new Roo.data.SimpleStore({
44315                 //fields : this.store.reader.meta.fields,
44316                 reader : this.store.reader,
44317                 data : [ ]
44318             });
44319         }
44320         this.stores[i]  = store;
44321                   
44322         var view = this.views[i] = new Roo.View(
44323             il,
44324             this.tpl,
44325             {
44326                 singleSelect:true,
44327                 store: store,
44328                 selectedClass: this.selectedClass
44329             }
44330         );
44331         view.getEl().setWidth(lw);
44332         view.getEl().setStyle({
44333             position: i < 1 ? 'relative' : 'absolute',
44334             top: 0,
44335             left: (i * lw ) + 'px',
44336             display : i > 0 ? 'none' : 'block'
44337         });
44338         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44339         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44340         //view.on('click', this.onViewClick, this, { list : i });
44341
44342         store.on('beforeload', this.onBeforeLoad, this);
44343         store.on('load',  this.onLoad, this, { list  : i});
44344         store.on('loadexception', this.onLoadException, this);
44345
44346         // hide the other vies..
44347         
44348         
44349         
44350     },
44351       
44352     restrictHeight : function()
44353     {
44354         var mh = 0;
44355         Roo.each(this.innerLists, function(il,i) {
44356             var el = this.views[i].getEl();
44357             el.dom.style.height = '';
44358             var inner = el.dom;
44359             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44360             // only adjust heights on other ones..
44361             mh = Math.max(h, mh);
44362             if (i < 1) {
44363                 
44364                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44365                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44366                
44367             }
44368             
44369             
44370         }, this);
44371         
44372         this.list.beginUpdate();
44373         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44374         this.list.alignTo(this.el, this.listAlign);
44375         this.list.endUpdate();
44376         
44377     },
44378      
44379     
44380     // -- store handlers..
44381     // private
44382     onBeforeLoad : function()
44383     {
44384         if(!this.hasFocus){
44385             return;
44386         }
44387         this.innerLists[0].update(this.loadingText ?
44388                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44389         this.restrictHeight();
44390         this.selectedIndex = -1;
44391     },
44392     // private
44393     onLoad : function(a,b,c,d)
44394     {
44395         if (!this.loadingChildren) {
44396             // then we are loading the top level. - hide the children
44397             for (var i = 1;i < this.views.length; i++) {
44398                 this.views[i].getEl().setStyle({ display : 'none' });
44399             }
44400             var lw = Math.floor(
44401                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44402             );
44403         
44404              this.list.setWidth(lw); // default to '1'
44405
44406             
44407         }
44408         if(!this.hasFocus){
44409             return;
44410         }
44411         
44412         if(this.store.getCount() > 0) {
44413             this.expand();
44414             this.restrictHeight();   
44415         } else {
44416             this.onEmptyResults();
44417         }
44418         
44419         if (!this.loadingChildren) {
44420             this.selectActive();
44421         }
44422         /*
44423         this.stores[1].loadData([]);
44424         this.stores[2].loadData([]);
44425         this.views
44426         */    
44427     
44428         //this.el.focus();
44429     },
44430     
44431     
44432     // private
44433     onLoadException : function()
44434     {
44435         this.collapse();
44436         Roo.log(this.store.reader.jsonData);
44437         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44438             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44439         }
44440         
44441         
44442     },
44443     // no cleaning of leading spaces on blur here.
44444     cleanLeadingSpace : function(e) { },
44445     
44446
44447     onSelectChange : function (view, sels, opts )
44448     {
44449         var ix = view.getSelectedIndexes();
44450          
44451         if (opts.list > this.maxColumns - 2) {
44452             if (view.store.getCount()<  1) {
44453                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44454
44455             } else  {
44456                 if (ix.length) {
44457                     // used to clear ?? but if we are loading unselected 
44458                     this.setFromData(view.store.getAt(ix[0]).data);
44459                 }
44460                 
44461             }
44462             
44463             return;
44464         }
44465         
44466         if (!ix.length) {
44467             // this get's fired when trigger opens..
44468            // this.setFromData({});
44469             var str = this.stores[opts.list+1];
44470             str.data.clear(); // removeall wihtout the fire events..
44471             return;
44472         }
44473         
44474         var rec = view.store.getAt(ix[0]);
44475          
44476         this.setFromData(rec.data);
44477         this.fireEvent('select', this, rec, ix[0]);
44478         
44479         var lw = Math.floor(
44480              (
44481                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44482              ) / this.maxColumns
44483         );
44484         this.loadingChildren = true;
44485         this.stores[opts.list+1].loadDataFromChildren( rec );
44486         this.loadingChildren = false;
44487         var dl = this.stores[opts.list+1]. getTotalCount();
44488         
44489         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44490         
44491         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44492         for (var i = opts.list+2; i < this.views.length;i++) {
44493             this.views[i].getEl().setStyle({ display : 'none' });
44494         }
44495         
44496         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44497         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44498         
44499         if (this.isLoading) {
44500            // this.selectActive(opts.list);
44501         }
44502          
44503     },
44504     
44505     
44506     
44507     
44508     onDoubleClick : function()
44509     {
44510         this.collapse(); //??
44511     },
44512     
44513      
44514     
44515     
44516     
44517     // private
44518     recordToStack : function(store, prop, value, stack)
44519     {
44520         var cstore = new Roo.data.SimpleStore({
44521             //fields : this.store.reader.meta.fields, // we need array reader.. for
44522             reader : this.store.reader,
44523             data : [ ]
44524         });
44525         var _this = this;
44526         var record  = false;
44527         var srec = false;
44528         if(store.getCount() < 1){
44529             return false;
44530         }
44531         store.each(function(r){
44532             if(r.data[prop] == value){
44533                 record = r;
44534             srec = r;
44535                 return false;
44536             }
44537             if (r.data.cn && r.data.cn.length) {
44538                 cstore.loadDataFromChildren( r);
44539                 var cret = _this.recordToStack(cstore, prop, value, stack);
44540                 if (cret !== false) {
44541                     record = cret;
44542                     srec = r;
44543                     return false;
44544                 }
44545             }
44546              
44547             return true;
44548         });
44549         if (record == false) {
44550             return false
44551         }
44552         stack.unshift(srec);
44553         return record;
44554     },
44555     
44556     /*
44557      * find the stack of stores that match our value.
44558      *
44559      * 
44560      */
44561     
44562     selectActive : function ()
44563     {
44564         // if store is not loaded, then we will need to wait for that to happen first.
44565         var stack = [];
44566         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44567         for (var i = 0; i < stack.length; i++ ) {
44568             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44569         }
44570         
44571     }
44572         
44573          
44574     
44575     
44576     
44577     
44578 });/*
44579  * Based on:
44580  * Ext JS Library 1.1.1
44581  * Copyright(c) 2006-2007, Ext JS, LLC.
44582  *
44583  * Originally Released Under LGPL - original licence link has changed is not relivant.
44584  *
44585  * Fork - LGPL
44586  * <script type="text/javascript">
44587  */
44588 /**
44589  * @class Roo.form.Checkbox
44590  * @extends Roo.form.Field
44591  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44592  * @constructor
44593  * Creates a new Checkbox
44594  * @param {Object} config Configuration options
44595  */
44596 Roo.form.Checkbox = function(config){
44597     Roo.form.Checkbox.superclass.constructor.call(this, config);
44598     this.addEvents({
44599         /**
44600          * @event check
44601          * Fires when the checkbox is checked or unchecked.
44602              * @param {Roo.form.Checkbox} this This checkbox
44603              * @param {Boolean} checked The new checked value
44604              */
44605         check : true
44606     });
44607 };
44608
44609 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44610     /**
44611      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44612      */
44613     focusClass : undefined,
44614     /**
44615      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44616      */
44617     fieldClass: "x-form-field",
44618     /**
44619      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44620      */
44621     checked: false,
44622     /**
44623      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44624      * {tag: "input", type: "checkbox", autocomplete: "off"})
44625      */
44626     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44627     /**
44628      * @cfg {String} boxLabel The text that appears beside the checkbox
44629      */
44630     boxLabel : "",
44631     /**
44632      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44633      */  
44634     inputValue : '1',
44635     /**
44636      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44637      */
44638      valueOff: '0', // value when not checked..
44639
44640     actionMode : 'viewEl', 
44641     //
44642     // private
44643     itemCls : 'x-menu-check-item x-form-item',
44644     groupClass : 'x-menu-group-item',
44645     inputType : 'hidden',
44646     
44647     
44648     inSetChecked: false, // check that we are not calling self...
44649     
44650     inputElement: false, // real input element?
44651     basedOn: false, // ????
44652     
44653     isFormField: true, // not sure where this is needed!!!!
44654
44655     onResize : function(){
44656         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44657         if(!this.boxLabel){
44658             this.el.alignTo(this.wrap, 'c-c');
44659         }
44660     },
44661
44662     initEvents : function(){
44663         Roo.form.Checkbox.superclass.initEvents.call(this);
44664         this.el.on("click", this.onClick,  this);
44665         this.el.on("change", this.onClick,  this);
44666     },
44667
44668
44669     getResizeEl : function(){
44670         return this.wrap;
44671     },
44672
44673     getPositionEl : function(){
44674         return this.wrap;
44675     },
44676
44677     // private
44678     onRender : function(ct, position){
44679         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44680         /*
44681         if(this.inputValue !== undefined){
44682             this.el.dom.value = this.inputValue;
44683         }
44684         */
44685         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44686         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44687         var viewEl = this.wrap.createChild({ 
44688             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44689         this.viewEl = viewEl;   
44690         this.wrap.on('click', this.onClick,  this); 
44691         
44692         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44693         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44694         
44695         
44696         
44697         if(this.boxLabel){
44698             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44699         //    viewEl.on('click', this.onClick,  this); 
44700         }
44701         //if(this.checked){
44702             this.setChecked(this.checked);
44703         //}else{
44704             //this.checked = this.el.dom;
44705         //}
44706
44707     },
44708
44709     // private
44710     initValue : Roo.emptyFn,
44711
44712     /**
44713      * Returns the checked state of the checkbox.
44714      * @return {Boolean} True if checked, else false
44715      */
44716     getValue : function(){
44717         if(this.el){
44718             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44719         }
44720         return this.valueOff;
44721         
44722     },
44723
44724         // private
44725     onClick : function(){ 
44726         if (this.disabled) {
44727             return;
44728         }
44729         this.setChecked(!this.checked);
44730
44731         //if(this.el.dom.checked != this.checked){
44732         //    this.setValue(this.el.dom.checked);
44733        // }
44734     },
44735
44736     /**
44737      * Sets the checked state of the checkbox.
44738      * On is always based on a string comparison between inputValue and the param.
44739      * @param {Boolean/String} value - the value to set 
44740      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44741      */
44742     setValue : function(v,suppressEvent){
44743         
44744         
44745         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44746         //if(this.el && this.el.dom){
44747         //    this.el.dom.checked = this.checked;
44748         //    this.el.dom.defaultChecked = this.checked;
44749         //}
44750         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44751         //this.fireEvent("check", this, this.checked);
44752     },
44753     // private..
44754     setChecked : function(state,suppressEvent)
44755     {
44756         if (this.inSetChecked) {
44757             this.checked = state;
44758             return;
44759         }
44760         
44761     
44762         if(this.wrap){
44763             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44764         }
44765         this.checked = state;
44766         if(suppressEvent !== true){
44767             this.fireEvent('check', this, state);
44768         }
44769         this.inSetChecked = true;
44770         this.el.dom.value = state ? this.inputValue : this.valueOff;
44771         this.inSetChecked = false;
44772         
44773     },
44774     // handle setting of hidden value by some other method!!?!?
44775     setFromHidden: function()
44776     {
44777         if(!this.el){
44778             return;
44779         }
44780         //console.log("SET FROM HIDDEN");
44781         //alert('setFrom hidden');
44782         this.setValue(this.el.dom.value);
44783     },
44784     
44785     onDestroy : function()
44786     {
44787         if(this.viewEl){
44788             Roo.get(this.viewEl).remove();
44789         }
44790          
44791         Roo.form.Checkbox.superclass.onDestroy.call(this);
44792     },
44793     
44794     setBoxLabel : function(str)
44795     {
44796         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44797     }
44798
44799 });/*
44800  * Based on:
44801  * Ext JS Library 1.1.1
44802  * Copyright(c) 2006-2007, Ext JS, LLC.
44803  *
44804  * Originally Released Under LGPL - original licence link has changed is not relivant.
44805  *
44806  * Fork - LGPL
44807  * <script type="text/javascript">
44808  */
44809  
44810 /**
44811  * @class Roo.form.Radio
44812  * @extends Roo.form.Checkbox
44813  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44814  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44815  * @constructor
44816  * Creates a new Radio
44817  * @param {Object} config Configuration options
44818  */
44819 Roo.form.Radio = function(){
44820     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44821 };
44822 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44823     inputType: 'radio',
44824
44825     /**
44826      * If this radio is part of a group, it will return the selected value
44827      * @return {String}
44828      */
44829     getGroupValue : function(){
44830         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44831     },
44832     
44833     
44834     onRender : function(ct, position){
44835         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44836         
44837         if(this.inputValue !== undefined){
44838             this.el.dom.value = this.inputValue;
44839         }
44840          
44841         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44842         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44843         //var viewEl = this.wrap.createChild({ 
44844         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44845         //this.viewEl = viewEl;   
44846         //this.wrap.on('click', this.onClick,  this); 
44847         
44848         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44849         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44850         
44851         
44852         
44853         if(this.boxLabel){
44854             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44855         //    viewEl.on('click', this.onClick,  this); 
44856         }
44857          if(this.checked){
44858             this.el.dom.checked =   'checked' ;
44859         }
44860          
44861     } 
44862     
44863     
44864 });Roo.rtf = {}; // namespace
44865 Roo.rtf.Hex = function(hex)
44866 {
44867     this.hexstr = hex;
44868 };
44869 Roo.rtf.Paragraph = function(opts)
44870 {
44871     this.content = []; ///??? is that used?
44872 };Roo.rtf.Span = function(opts)
44873 {
44874     this.value = opts.value;
44875 };
44876
44877 Roo.rtf.Group = function(parent)
44878 {
44879     // we dont want to acutally store parent - it will make debug a nightmare..
44880     this.content = [];
44881     this.cn  = [];
44882      
44883        
44884     
44885 };
44886
44887 Roo.rtf.Group.prototype = {
44888     ignorable : false,
44889     content: false,
44890     cn: false,
44891     addContent : function(node) {
44892         // could set styles...
44893         this.content.push(node);
44894     },
44895     addChild : function(cn)
44896     {
44897         this.cn.push(cn);
44898     },
44899     // only for images really...
44900     toDataURL : function()
44901     {
44902         var mimetype = false;
44903         switch(true) {
44904             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44905                 mimetype = "image/png";
44906                 break;
44907              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44908                 mimetype = "image/jpeg";
44909                 break;
44910             default :
44911                 return 'about:blank'; // ?? error?
44912         }
44913         
44914         
44915         var hexstring = this.content[this.content.length-1].value;
44916         
44917         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44918             return String.fromCharCode(parseInt(a, 16));
44919         }).join(""));
44920     }
44921     
44922 };
44923 // this looks like it's normally the {rtf{ .... }}
44924 Roo.rtf.Document = function()
44925 {
44926     // we dont want to acutally store parent - it will make debug a nightmare..
44927     this.rtlch  = [];
44928     this.content = [];
44929     this.cn = [];
44930     
44931 };
44932 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44933     addChild : function(cn)
44934     {
44935         this.cn.push(cn);
44936         switch(cn.type) {
44937             case 'rtlch': // most content seems to be inside this??
44938             case 'listtext':
44939             case 'shpinst':
44940                 this.rtlch.push(cn);
44941                 return;
44942             default:
44943                 this[cn.type] = cn;
44944         }
44945         
44946     },
44947     
44948     getElementsByType : function(type)
44949     {
44950         var ret =  [];
44951         this._getElementsByType(type, ret, this.cn, 'rtf');
44952         return ret;
44953     },
44954     _getElementsByType : function (type, ret, search_array, path)
44955     {
44956         search_array.forEach(function(n,i) {
44957             if (n.type == type) {
44958                 n.path = path + '/' + n.type + ':' + i;
44959                 ret.push(n);
44960             }
44961             if (n.cn.length > 0) {
44962                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44963             }
44964         },this);
44965     }
44966     
44967 });
44968  
44969 Roo.rtf.Ctrl = function(opts)
44970 {
44971     this.value = opts.value;
44972     this.param = opts.param;
44973 };
44974 /**
44975  *
44976  *
44977  * based on this https://github.com/iarna/rtf-parser
44978  * it's really only designed to extract pict from pasted RTF 
44979  *
44980  * usage:
44981  *
44982  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44983  *  
44984  *
44985  */
44986
44987  
44988
44989
44990
44991 Roo.rtf.Parser = function(text) {
44992     //super({objectMode: true})
44993     this.text = '';
44994     this.parserState = this.parseText;
44995     
44996     // these are for interpeter...
44997     this.doc = {};
44998     ///this.parserState = this.parseTop
44999     this.groupStack = [];
45000     this.hexStore = [];
45001     this.doc = false;
45002     
45003     this.groups = []; // where we put the return.
45004     
45005     for (var ii = 0; ii < text.length; ++ii) {
45006         ++this.cpos;
45007         
45008         if (text[ii] === '\n') {
45009             ++this.row;
45010             this.col = 1;
45011         } else {
45012             ++this.col;
45013         }
45014         this.parserState(text[ii]);
45015     }
45016     
45017     
45018     
45019 };
45020 Roo.rtf.Parser.prototype = {
45021     text : '', // string being parsed..
45022     controlWord : '',
45023     controlWordParam :  '',
45024     hexChar : '',
45025     doc : false,
45026     group: false,
45027     groupStack : false,
45028     hexStore : false,
45029     
45030     
45031     cpos : 0, 
45032     row : 1, // reportin?
45033     col : 1, //
45034
45035      
45036     push : function (el)
45037     {
45038         var m = 'cmd'+ el.type;
45039         if (typeof(this[m]) == 'undefined') {
45040             Roo.log('invalid cmd:' + el.type);
45041             return;
45042         }
45043         this[m](el);
45044         //Roo.log(el);
45045     },
45046     flushHexStore : function()
45047     {
45048         if (this.hexStore.length < 1) {
45049             return;
45050         }
45051         var hexstr = this.hexStore.map(
45052             function(cmd) {
45053                 return cmd.value;
45054         }).join('');
45055         
45056         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45057               
45058             
45059         this.hexStore.splice(0)
45060         
45061     },
45062     
45063     cmdgroupstart : function()
45064     {
45065         this.flushHexStore();
45066         if (this.group) {
45067             this.groupStack.push(this.group);
45068         }
45069          // parent..
45070         if (this.doc === false) {
45071             this.group = this.doc = new Roo.rtf.Document();
45072             return;
45073             
45074         }
45075         this.group = new Roo.rtf.Group(this.group);
45076     },
45077     cmdignorable : function()
45078     {
45079         this.flushHexStore();
45080         this.group.ignorable = true;
45081     },
45082     cmdendparagraph : function()
45083     {
45084         this.flushHexStore();
45085         this.group.addContent(new Roo.rtf.Paragraph());
45086     },
45087     cmdgroupend : function ()
45088     {
45089         this.flushHexStore();
45090         var endingGroup = this.group;
45091         
45092         
45093         this.group = this.groupStack.pop();
45094         if (this.group) {
45095             this.group.addChild(endingGroup);
45096         }
45097         
45098         
45099         
45100         var doc = this.group || this.doc;
45101         //if (endingGroup instanceof FontTable) {
45102         //  doc.fonts = endingGroup.table
45103         //} else if (endingGroup instanceof ColorTable) {
45104         //  doc.colors = endingGroup.table
45105         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45106         if (endingGroup.ignorable === false) {
45107             //code
45108             this.groups.push(endingGroup);
45109            // Roo.log( endingGroup );
45110         }
45111             //Roo.each(endingGroup.content, function(item)) {
45112             //    doc.addContent(item);
45113             //}
45114             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45115         //}
45116     },
45117     cmdtext : function (cmd)
45118     {
45119         this.flushHexStore();
45120         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45121             //this.group = this.doc
45122         }
45123         this.group.addContent(new Roo.rtf.Span(cmd));
45124     },
45125     cmdcontrolword : function (cmd)
45126     {
45127         this.flushHexStore();
45128         if (!this.group.type) {
45129             this.group.type = cmd.value;
45130             return;
45131         }
45132         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45133         // we actually don't care about ctrl words...
45134         return ;
45135         /*
45136         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45137         if (this[method]) {
45138             this[method](cmd.param)
45139         } else {
45140             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45141         }
45142         */
45143     },
45144     cmdhexchar : function(cmd) {
45145         this.hexStore.push(cmd);
45146     },
45147     cmderror : function(cmd) {
45148         throw new Exception (cmd.value);
45149     },
45150     
45151     /*
45152       _flush (done) {
45153         if (this.text !== '\u0000') this.emitText()
45154         done()
45155       }
45156       */
45157       
45158       
45159     parseText : function(c)
45160     {
45161         if (c === '\\') {
45162             this.parserState = this.parseEscapes;
45163         } else if (c === '{') {
45164             this.emitStartGroup();
45165         } else if (c === '}') {
45166             this.emitEndGroup();
45167         } else if (c === '\x0A' || c === '\x0D') {
45168             // cr/lf are noise chars
45169         } else {
45170             this.text += c;
45171         }
45172     },
45173     
45174     parseEscapes: function (c)
45175     {
45176         if (c === '\\' || c === '{' || c === '}') {
45177             this.text += c;
45178             this.parserState = this.parseText;
45179         } else {
45180             this.parserState = this.parseControlSymbol;
45181             this.parseControlSymbol(c);
45182         }
45183     },
45184     parseControlSymbol: function(c)
45185     {
45186         if (c === '~') {
45187             this.text += '\u00a0'; // nbsp
45188             this.parserState = this.parseText
45189         } else if (c === '-') {
45190              this.text += '\u00ad'; // soft hyphen
45191         } else if (c === '_') {
45192             this.text += '\u2011'; // non-breaking hyphen
45193         } else if (c === '*') {
45194             this.emitIgnorable();
45195             this.parserState = this.parseText;
45196         } else if (c === "'") {
45197             this.parserState = this.parseHexChar;
45198         } else if (c === '|') { // formula cacter
45199             this.emitFormula();
45200             this.parserState = this.parseText;
45201         } else if (c === ':') { // subentry in an index entry
45202             this.emitIndexSubEntry();
45203             this.parserState = this.parseText;
45204         } else if (c === '\x0a') {
45205             this.emitEndParagraph();
45206             this.parserState = this.parseText;
45207         } else if (c === '\x0d') {
45208             this.emitEndParagraph();
45209             this.parserState = this.parseText;
45210         } else {
45211             this.parserState = this.parseControlWord;
45212             this.parseControlWord(c);
45213         }
45214     },
45215     parseHexChar: function (c)
45216     {
45217         if (/^[A-Fa-f0-9]$/.test(c)) {
45218             this.hexChar += c;
45219             if (this.hexChar.length >= 2) {
45220               this.emitHexChar();
45221               this.parserState = this.parseText;
45222             }
45223             return;
45224         }
45225         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45226         this.parserState = this.parseText;
45227         
45228     },
45229     parseControlWord : function(c)
45230     {
45231         if (c === ' ') {
45232             this.emitControlWord();
45233             this.parserState = this.parseText;
45234         } else if (/^[-\d]$/.test(c)) {
45235             this.parserState = this.parseControlWordParam;
45236             this.controlWordParam += c;
45237         } else if (/^[A-Za-z]$/.test(c)) {
45238           this.controlWord += c;
45239         } else {
45240           this.emitControlWord();
45241           this.parserState = this.parseText;
45242           this.parseText(c);
45243         }
45244     },
45245     parseControlWordParam : function (c) {
45246         if (/^\d$/.test(c)) {
45247           this.controlWordParam += c;
45248         } else if (c === ' ') {
45249           this.emitControlWord();
45250           this.parserState = this.parseText;
45251         } else {
45252           this.emitControlWord();
45253           this.parserState = this.parseText;
45254           this.parseText(c);
45255         }
45256     },
45257     
45258     
45259     
45260     
45261     emitText : function () {
45262         if (this.text === '') {
45263             return;
45264         }
45265         this.push({
45266             type: 'text',
45267             value: this.text,
45268             pos: this.cpos,
45269             row: this.row,
45270             col: this.col
45271         });
45272         this.text = ''
45273     },
45274     emitControlWord : function ()
45275     {
45276         this.emitText();
45277         if (this.controlWord === '') {
45278             this.emitError('empty control word');
45279         } else {
45280             this.push({
45281                   type: 'controlword',
45282                   value: this.controlWord,
45283                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45284                   pos: this.cpos,
45285                   row: this.row,
45286                   col: this.col
45287             });
45288         }
45289         this.controlWord = '';
45290         this.controlWordParam = '';
45291     },
45292     emitStartGroup : function ()
45293     {
45294         this.emitText();
45295         this.push({
45296             type: 'groupstart',
45297             pos: this.cpos,
45298             row: this.row,
45299             col: this.col
45300         });
45301     },
45302     emitEndGroup : function ()
45303     {
45304         this.emitText();
45305         this.push({
45306             type: 'groupend',
45307             pos: this.cpos,
45308             row: this.row,
45309             col: this.col
45310         });
45311     },
45312     emitIgnorable : function ()
45313     {
45314         this.emitText();
45315         this.push({
45316             type: 'ignorable',
45317             pos: this.cpos,
45318             row: this.row,
45319             col: this.col
45320         });
45321     },
45322     emitHexChar : function ()
45323     {
45324         this.emitText();
45325         this.push({
45326             type: 'hexchar',
45327             value: this.hexChar,
45328             pos: this.cpos,
45329             row: this.row,
45330             col: this.col
45331         });
45332         this.hexChar = ''
45333     },
45334     emitError : function (message)
45335     {
45336       this.emitText();
45337       this.push({
45338             type: 'error',
45339             value: message,
45340             row: this.row,
45341             col: this.col,
45342             char: this.cpos //,
45343             //stack: new Error().stack
45344         });
45345     },
45346     emitEndParagraph : function () {
45347         this.emitText();
45348         this.push({
45349             type: 'endparagraph',
45350             pos: this.cpos,
45351             row: this.row,
45352             col: this.col
45353         });
45354     }
45355      
45356 } ;
45357 Roo.htmleditor = {};
45358  
45359 /**
45360  * @class Roo.htmleditor.Filter
45361  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45362  * @cfg {DomElement} node The node to iterate and filter
45363  * @cfg {boolean|String|Array} tag Tags to replace 
45364  * @constructor
45365  * Create a new Filter.
45366  * @param {Object} config Configuration options
45367  */
45368
45369
45370
45371 Roo.htmleditor.Filter = function(cfg) {
45372     Roo.apply(this.cfg);
45373     // this does not actually call walk as it's really just a abstract class
45374 }
45375
45376
45377 Roo.htmleditor.Filter.prototype = {
45378     
45379     node: false,
45380     
45381     tag: false,
45382
45383     // overrride to do replace comments.
45384     replaceComment : false,
45385     
45386     // overrride to do replace or do stuff with tags..
45387     replaceTag : false,
45388     
45389     walk : function(dom)
45390     {
45391         Roo.each( Array.from(dom.childNodes), function( e ) {
45392             switch(true) {
45393                 
45394                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45395                     this.replaceComment(e);
45396                     return;
45397                 
45398                 case e.nodeType != 1: //not a node.
45399                     return;
45400                 
45401                 case this.tag === true: // everything
45402                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45403                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45404                     if (this.replaceTag && false === this.replaceTag(e)) {
45405                         return;
45406                     }
45407                     if (e.hasChildNodes()) {
45408                         this.walk(e);
45409                     }
45410                     return;
45411                 
45412                 default:    // tags .. that do not match.
45413                     if (e.hasChildNodes()) {
45414                         this.walk(e);
45415                     }
45416             }
45417             
45418         }, this);
45419         
45420     }
45421 }; 
45422
45423 /**
45424  * @class Roo.htmleditor.FilterAttributes
45425  * clean attributes and  styles including http:// etc.. in attribute
45426  * @constructor
45427 * Run a new Attribute Filter
45428 * @param {Object} config Configuration options
45429  */
45430 Roo.htmleditor.FilterAttributes = function(cfg)
45431 {
45432     Roo.apply(this, cfg);
45433     this.attrib_black = this.attrib_black || [];
45434     this.attrib_white = this.attrib_white || [];
45435
45436     this.attrib_clean = this.attrib_clean || [];
45437     this.style_white = this.style_white || [];
45438     this.style_black = this.style_black || [];
45439     this.walk(cfg.node);
45440 }
45441
45442 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45443 {
45444     tag: true, // all tags
45445     
45446     attrib_black : false, // array
45447     attrib_clean : false,
45448     attrib_white : false,
45449
45450     style_white : false,
45451     style_black : false,
45452      
45453      
45454     replaceTag : function(node)
45455     {
45456         if (!node.attributes || !node.attributes.length) {
45457             return true;
45458         }
45459         
45460         for (var i = node.attributes.length-1; i > -1 ; i--) {
45461             var a = node.attributes[i];
45462             //console.log(a);
45463             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45464                 node.removeAttribute(a.name);
45465                 continue;
45466             }
45467             
45468             
45469             
45470             if (a.name.toLowerCase().substr(0,2)=='on')  {
45471                 node.removeAttribute(a.name);
45472                 continue;
45473             }
45474             
45475             
45476             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45477                 node.removeAttribute(a.name);
45478                 continue;
45479             }
45480             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45481                 this.cleanAttr(node,a.name,a.value); // fixme..
45482                 continue;
45483             }
45484             if (a.name == 'style') {
45485                 this.cleanStyle(node,a.name,a.value);
45486                 continue;
45487             }
45488             /// clean up MS crap..
45489             // tecnically this should be a list of valid class'es..
45490             
45491             
45492             if (a.name == 'class') {
45493                 if (a.value.match(/^Mso/)) {
45494                     node.removeAttribute('class');
45495                 }
45496                 
45497                 if (a.value.match(/^body$/)) {
45498                     node.removeAttribute('class');
45499                 }
45500                 continue;
45501             }
45502             
45503             
45504             // style cleanup!?
45505             // class cleanup?
45506             
45507         }
45508         return true; // clean children
45509     },
45510         
45511     cleanAttr: function(node, n,v)
45512     {
45513         
45514         if (v.match(/^\./) || v.match(/^\//)) {
45515             return;
45516         }
45517         if (v.match(/^(http|https):\/\//)
45518             || v.match(/^mailto:/) 
45519             || v.match(/^ftp:/)
45520             || v.match(/^data:/)
45521             ) {
45522             return;
45523         }
45524         if (v.match(/^#/)) {
45525             return;
45526         }
45527         if (v.match(/^\{/)) { // allow template editing.
45528             return;
45529         }
45530 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45531         node.removeAttribute(n);
45532         
45533     },
45534     cleanStyle : function(node,  n,v)
45535     {
45536         if (v.match(/expression/)) { //XSS?? should we even bother..
45537             node.removeAttribute(n);
45538             return;
45539         }
45540         
45541         var parts = v.split(/;/);
45542         var clean = [];
45543         
45544         Roo.each(parts, function(p) {
45545             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45546             if (!p.length) {
45547                 return true;
45548             }
45549             var l = p.split(':').shift().replace(/\s+/g,'');
45550             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45551             
45552             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45553                 return true;
45554             }
45555             //Roo.log()
45556             // only allow 'c whitelisted system attributes'
45557             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45558                 return true;
45559             }
45560             
45561             
45562             clean.push(p);
45563             return true;
45564         },this);
45565         if (clean.length) { 
45566             node.setAttribute(n, clean.join(';'));
45567         } else {
45568             node.removeAttribute(n);
45569         }
45570         
45571     }
45572         
45573         
45574         
45575     
45576 });/**
45577  * @class Roo.htmleditor.FilterBlack
45578  * remove blacklisted elements.
45579  * @constructor
45580  * Run a new Blacklisted Filter
45581  * @param {Object} config Configuration options
45582  */
45583
45584 Roo.htmleditor.FilterBlack = function(cfg)
45585 {
45586     Roo.apply(this, cfg);
45587     this.walk(cfg.node);
45588 }
45589
45590 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45591 {
45592     tag : true, // all elements.
45593    
45594     replace : function(n)
45595     {
45596         n.parentNode.removeChild(n);
45597     }
45598 });
45599 /**
45600  * @class Roo.htmleditor.FilterComment
45601  * remove comments.
45602  * @constructor
45603 * Run a new Comments Filter
45604 * @param {Object} config Configuration options
45605  */
45606 Roo.htmleditor.FilterComment = function(cfg)
45607 {
45608     this.walk(cfg.node);
45609 }
45610
45611 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45612 {
45613   
45614     replaceComment : function(n)
45615     {
45616         n.parentNode.removeChild(n);
45617     }
45618 });/**
45619  * @class Roo.htmleditor.FilterKeepChildren
45620  * remove tags but keep children
45621  * @constructor
45622  * Run a new Keep Children Filter
45623  * @param {Object} config Configuration options
45624  */
45625
45626 Roo.htmleditor.FilterKeepChildren = function(cfg)
45627 {
45628     Roo.apply(this, cfg);
45629     if (this.tag === false) {
45630         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45631     }
45632     this.walk(cfg.node);
45633 }
45634
45635 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45636 {
45637     
45638   
45639     replaceTag : function(node)
45640     {
45641         // walk children...
45642         //Roo.log(node);
45643         var ar = Array.from(node.childNodes);
45644         //remove first..
45645         for (var i = 0; i < ar.length; i++) {
45646             if (ar[i].nodeType == 1) {
45647                 if (
45648                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45649                     || // array and it matches
45650                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45651                 ) {
45652                     this.replaceTag(ar[i]); // child is blacklisted as well...
45653                     continue;
45654                 }
45655             }
45656         }  
45657         ar = Array.from(node.childNodes);
45658         for (var i = 0; i < ar.length; i++) {
45659          
45660             node.removeChild(ar[i]);
45661             // what if we need to walk these???
45662             node.parentNode.insertBefore(ar[i], node);
45663             if (this.tag !== false) {
45664                 this.walk(ar[i]);
45665                 
45666             }
45667         }
45668         node.parentNode.removeChild(node);
45669         return false; // don't walk children
45670         
45671         
45672     }
45673 });/**
45674  * @class Roo.htmleditor.FilterParagraph
45675  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45676  * like on 'push' to remove the <p> tags and replace them with line breaks.
45677  * @constructor
45678  * Run a new Paragraph Filter
45679  * @param {Object} config Configuration options
45680  */
45681
45682 Roo.htmleditor.FilterParagraph = function(cfg)
45683 {
45684     // no need to apply config.
45685     this.walk(cfg.node);
45686 }
45687
45688 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45689 {
45690     
45691      
45692     tag : 'P',
45693     
45694      
45695     replaceTag : function(node)
45696     {
45697         
45698         if (node.childNodes.length == 1 &&
45699             node.childNodes[0].nodeType == 3 &&
45700             node.childNodes[0].textContent.trim().length < 1
45701             ) {
45702             // remove and replace with '<BR>';
45703             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45704             return false; // no need to walk..
45705         }
45706         var ar = Array.from(node.childNodes);
45707         for (var i = 0; i < ar.length; i++) {
45708             node.removeChild(ar[i]);
45709             // what if we need to walk these???
45710             node.parentNode.insertBefore(ar[i], node);
45711         }
45712         // now what about this?
45713         // <p> &nbsp; </p>
45714         
45715         // double BR.
45716         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45717         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45718         node.parentNode.removeChild(node);
45719         
45720         return false;
45721
45722     }
45723     
45724 });/**
45725  * @class Roo.htmleditor.FilterSpan
45726  * filter span's with no attributes out..
45727  * @constructor
45728  * Run a new Span Filter
45729  * @param {Object} config Configuration options
45730  */
45731
45732 Roo.htmleditor.FilterSpan = function(cfg)
45733 {
45734     // no need to apply config.
45735     this.walk(cfg.node);
45736 }
45737
45738 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45739 {
45740      
45741     tag : 'SPAN',
45742      
45743  
45744     replaceTag : function(node)
45745     {
45746         if (node.attributes && node.attributes.length > 0) {
45747             return true; // walk if there are any.
45748         }
45749         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45750         return false;
45751      
45752     }
45753     
45754 });/**
45755  * @class Roo.htmleditor.FilterTableWidth
45756   try and remove table width data - as that frequently messes up other stuff.
45757  * 
45758  *      was cleanTableWidths.
45759  *
45760  * Quite often pasting from word etc.. results in tables with column and widths.
45761  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45762  *
45763  * @constructor
45764  * Run a new Table Filter
45765  * @param {Object} config Configuration options
45766  */
45767
45768 Roo.htmleditor.FilterTableWidth = function(cfg)
45769 {
45770     // no need to apply config.
45771     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45772     this.walk(cfg.node);
45773 }
45774
45775 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45776 {
45777      
45778      
45779     
45780     replaceTag: function(node) {
45781         
45782         
45783       
45784         if (node.hasAttribute('width')) {
45785             node.removeAttribute('width');
45786         }
45787         
45788          
45789         if (node.hasAttribute("style")) {
45790             // pretty basic...
45791             
45792             var styles = node.getAttribute("style").split(";");
45793             var nstyle = [];
45794             Roo.each(styles, function(s) {
45795                 if (!s.match(/:/)) {
45796                     return;
45797                 }
45798                 var kv = s.split(":");
45799                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45800                     return;
45801                 }
45802                 // what ever is left... we allow.
45803                 nstyle.push(s);
45804             });
45805             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45806             if (!nstyle.length) {
45807                 node.removeAttribute('style');
45808             }
45809         }
45810         
45811         return true; // continue doing children..
45812     }
45813 });/**
45814  * @class Roo.htmleditor.FilterWord
45815  * try and clean up all the mess that Word generates.
45816  * 
45817  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45818  
45819  * @constructor
45820  * Run a new Span Filter
45821  * @param {Object} config Configuration options
45822  */
45823
45824 Roo.htmleditor.FilterWord = function(cfg)
45825 {
45826     // no need to apply config.
45827     this.walk(cfg.node);
45828 }
45829
45830 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45831 {
45832     tag: true,
45833      
45834     
45835     /**
45836      * Clean up MS wordisms...
45837      */
45838     replaceTag : function(node)
45839     {
45840          
45841         // no idea what this does - span with text, replaceds with just text.
45842         if(
45843                 node.nodeName == 'SPAN' &&
45844                 !node.hasAttributes() &&
45845                 node.childNodes.length == 1 &&
45846                 node.firstChild.nodeName == "#text"  
45847         ) {
45848             var textNode = node.firstChild;
45849             node.removeChild(textNode);
45850             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45851                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45852             }
45853             node.parentNode.insertBefore(textNode, node);
45854             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45855                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45856             }
45857             
45858             node.parentNode.removeChild(node);
45859             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45860         }
45861         
45862    
45863         
45864         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45865             node.parentNode.removeChild(node);
45866             return false; // dont do chidlren
45867         }
45868         //Roo.log(node.tagName);
45869         // remove - but keep children..
45870         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45871             //Roo.log('-- removed');
45872             while (node.childNodes.length) {
45873                 var cn = node.childNodes[0];
45874                 node.removeChild(cn);
45875                 node.parentNode.insertBefore(cn, node);
45876                 // move node to parent - and clean it..
45877                 this.replaceTag(cn);
45878             }
45879             node.parentNode.removeChild(node);
45880             /// no need to iterate chidlren = it's got none..
45881             //this.iterateChildren(node, this.cleanWord);
45882             return false; // no need to iterate children.
45883         }
45884         // clean styles
45885         if (node.className.length) {
45886             
45887             var cn = node.className.split(/\W+/);
45888             var cna = [];
45889             Roo.each(cn, function(cls) {
45890                 if (cls.match(/Mso[a-zA-Z]+/)) {
45891                     return;
45892                 }
45893                 cna.push(cls);
45894             });
45895             node.className = cna.length ? cna.join(' ') : '';
45896             if (!cna.length) {
45897                 node.removeAttribute("class");
45898             }
45899         }
45900         
45901         if (node.hasAttribute("lang")) {
45902             node.removeAttribute("lang");
45903         }
45904         
45905         if (node.hasAttribute("style")) {
45906             
45907             var styles = node.getAttribute("style").split(";");
45908             var nstyle = [];
45909             Roo.each(styles, function(s) {
45910                 if (!s.match(/:/)) {
45911                     return;
45912                 }
45913                 var kv = s.split(":");
45914                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45915                     return;
45916                 }
45917                 // what ever is left... we allow.
45918                 nstyle.push(s);
45919             });
45920             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45921             if (!nstyle.length) {
45922                 node.removeAttribute('style');
45923             }
45924         }
45925         return true; // do children
45926         
45927         
45928         
45929     }
45930 });
45931 /**
45932  * @class Roo.htmleditor.FilterStyleToTag
45933  * part of the word stuff... - certain 'styles' should be converted to tags.
45934  * eg.
45935  *   font-weight: bold -> bold
45936  *   ?? super / subscrit etc..
45937  * 
45938  * @constructor
45939 * Run a new style to tag filter.
45940 * @param {Object} config Configuration options
45941  */
45942 Roo.htmleditor.FilterStyleToTag = function(cfg)
45943 {
45944     
45945     this.tags = {
45946         B  : [ 'fontWeight' , 'bold'],
45947         I :  [ 'fontStyle' , 'italic'],
45948         //pre :  [ 'font-style' , 'italic'],
45949         // h1.. h6 ?? font-size?
45950         SUP : [ 'verticalAlign' , 'super' ],
45951         SUB : [ 'verticalAlign' , 'sub' ]
45952         
45953         
45954     };
45955     
45956     Roo.apply(this, cfg);
45957      
45958     
45959     this.walk(cfg.node);
45960     
45961     
45962     
45963 }
45964
45965
45966 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45967 {
45968     tag: true, // all tags
45969     
45970     tags : false,
45971     
45972     
45973     replaceTag : function(node)
45974     {
45975         
45976         
45977         if (node.getAttribute("style") === null) {
45978             return true;
45979         }
45980         var inject = [];
45981         for (var k in this.tags) {
45982             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45983                 inject.push(k);
45984                 node.style.removeProperty(this.tags[k][0]);
45985             }
45986         }
45987         if (!inject.length) {
45988             return true; 
45989         }
45990         var cn = Array.from(node.childNodes);
45991         var nn = node;
45992         Roo.each(inject, function(t) {
45993             var nc = node.ownerDocument.createelement(t);
45994             nn.appendChild(nc);
45995             nn = nc;
45996         });
45997         for(var i = 0;i < cn.length;cn++) {
45998             node.removeChild(cn[i]);
45999             nn.appendChild(cn[i]);
46000         }
46001         return true /// iterate thru
46002     }
46003     
46004 })/**
46005  * @class Roo.htmleditor.FilterLongBr
46006  * BR/BR/BR - keep a maximum of 2...
46007  * @constructor
46008  * Run a new Long BR Filter
46009  * @param {Object} config Configuration options
46010  */
46011
46012 Roo.htmleditor.FilterLongBr = function(cfg)
46013 {
46014     // no need to apply config.
46015     this.walk(cfg.node);
46016 }
46017
46018 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46019 {
46020     
46021      
46022     tag : 'BR',
46023     
46024      
46025     replaceTag : function(node)
46026     {
46027         
46028         var ps = node.nextSibling;
46029         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46030             ps = ps.nextSibling;
46031         }
46032         
46033         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46034             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46035             return false;
46036         }
46037         
46038         if (!ps || ps.nodeType != 1) {
46039             return false;
46040         }
46041         
46042         if (!ps || ps.tagName != 'BR') {
46043            
46044             return false;
46045         }
46046         
46047         
46048         
46049         
46050         
46051         if (!node.previousSibling) {
46052             return false;
46053         }
46054         var ps = node.previousSibling;
46055         
46056         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46057             ps = ps.previousSibling;
46058         }
46059         if (!ps || ps.nodeType != 1) {
46060             return false;
46061         }
46062         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46063         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46064             return false;
46065         }
46066         
46067         node.parentNode.removeChild(node); // remove me...
46068         
46069         return false; // no need to do children
46070
46071     }
46072     
46073 });
46074 /**
46075  * @class Roo.htmleditor.Tidy
46076  * Tidy HTML 
46077  * @cfg {Roo.HtmlEditorCore} core the editor.
46078  * @constructor
46079  * Create a new Filter.
46080  * @param {Object} config Configuration options
46081  */
46082
46083
46084 Roo.htmleditor.Tidy = function(cfg) {
46085     Roo.apply(this, cfg);
46086     
46087     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46088      
46089 }
46090
46091 Roo.htmleditor.Tidy.toString = function(node)
46092 {
46093     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46094 }
46095
46096 Roo.htmleditor.Tidy.prototype = {
46097     
46098     
46099     wrap : function(s) {
46100         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46101     },
46102
46103     
46104     tidy : function(node, indent) {
46105      
46106         if  (node.nodeType == 3) {
46107             // text.
46108             
46109             
46110             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46111                 
46112             
46113         }
46114         
46115         if  (node.nodeType != 1) {
46116             return '';
46117         }
46118         
46119         
46120         
46121         if (node.tagName == 'BODY') {
46122             
46123             return this.cn(node, '');
46124         }
46125              
46126              // Prints the node tagName, such as <A>, <IMG>, etc
46127         var ret = "<" + node.tagName +  this.attr(node) ;
46128         
46129         // elements with no children..
46130         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46131                 return ret + '/>';
46132         }
46133         ret += '>';
46134         
46135         
46136         var cindent = indent === false ? '' : (indent + '  ');
46137         // tags where we will not pad the children.. (inline text tags etc..)
46138         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46139             cindent = false;
46140             
46141             
46142         }
46143         
46144         var cn = this.cn(node, cindent );
46145         
46146         return ret + cn  + '</' + node.tagName + '>';
46147         
46148     },
46149     cn: function(node, indent)
46150     {
46151         var ret = [];
46152         
46153         var ar = Array.from(node.childNodes);
46154         for (var i = 0 ; i < ar.length ; i++) {
46155             
46156             
46157             
46158             if (indent !== false   // indent==false preservies everything
46159                 && i > 0
46160                 && ar[i].nodeType == 3 
46161                 && ar[i].nodeValue.length > 0
46162                 && ar[i].nodeValue.match(/^\s+/)
46163             ) {
46164                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46165                     ret.pop(); // remove line break from last?
46166                 }
46167                 
46168                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46169             }
46170             if (indent !== false
46171                 && ar[i].nodeType == 1 // element - and indent is not set... 
46172             ) {
46173                 ret.push("\n" + indent); 
46174             }
46175             
46176             ret.push(this.tidy(ar[i], indent));
46177             // text + trailing indent 
46178             if (indent !== false
46179                 && ar[i].nodeType == 3
46180                 && ar[i].nodeValue.length > 0
46181                 && ar[i].nodeValue.match(/\s+$/)
46182             ){
46183                 ret.push("\n" + indent); 
46184             }
46185             
46186             
46187             
46188             
46189         }
46190         // what if all text?
46191         
46192         
46193         return ret.join('');
46194     },
46195     
46196          
46197         
46198     attr : function(node)
46199     {
46200         var attr = [];
46201         for(i = 0; i < node.attributes.length;i++) {
46202             
46203             // skip empty values?
46204             if (!node.attributes.item(i).value.length) {
46205                 continue;
46206             }
46207             attr.push(  node.attributes.item(i).name + '="' +
46208                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46209             );
46210         }
46211         return attr.length ? (' ' + attr.join(' ') ) : '';
46212         
46213     }
46214     
46215     
46216     
46217 }
46218 /**
46219  * @class Roo.htmleditor.KeyEnter
46220  * Handle Enter press..
46221  * @cfg {Roo.HtmlEditorCore} core the editor.
46222  * @constructor
46223  * Create a new Filter.
46224  * @param {Object} config Configuration options
46225  */
46226
46227
46228
46229 Roo.htmleditor.KeyEnter = function(cfg) {
46230     Roo.apply(this, cfg);
46231     // this does not actually call walk as it's really just a abstract class
46232  
46233     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46234 }
46235
46236
46237 Roo.htmleditor.KeyEnter.prototype = {
46238     
46239     core : false,
46240     
46241     keypress : function(e) {
46242         if (e.charCode != 13) {
46243             return true;
46244         }
46245         e.preventDefault();
46246         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46247         var doc = this.core.doc;
46248         
46249         var docFragment = doc.createDocumentFragment();
46250     
46251         //add a new line
46252         var newEle = doc.createTextNode('\n');
46253         docFragment.appendChild(newEle);
46254     
46255     
46256         var range = this.core.win.getSelection().getRangeAt(0);
46257         var n = range.commonAncestorContainer ;
46258         while (n && n.nodeType != 1) {
46259             n  = n.parentNode;
46260         }
46261         var li = false;
46262         if (n && n.tagName == 'UL') {
46263             li = doc.createElement('LI');
46264             n.appendChild(li);
46265             
46266         }
46267         if (n && n.tagName == 'LI') {
46268             li = doc.createElement('LI');
46269             if (n.nextSibling) {
46270                 n.parentNode.insertBefore(li, n.firstSibling);
46271                 
46272             } else {
46273                 n.parentNode.appendChild(li);
46274             }
46275         }
46276         if (li) {   
46277             range = doc.createRange();
46278             range.setStartAfter(li);
46279             range.collapse(true);
46280         
46281             //make the cursor there
46282             var sel = this.core.win.getSelection();
46283             sel.removeAllRanges();
46284             sel.addRange(range);
46285             return false;
46286             
46287             
46288         }
46289         //add the br, or p, or something else
46290         newEle = doc.createElement('br');
46291         docFragment.appendChild(newEle);
46292     
46293         //make the br replace selection
46294         
46295         range.deleteContents();
46296         
46297         range.insertNode(docFragment);
46298     
46299         //create a new range
46300         range = doc.createRange();
46301         range.setStartAfter(newEle);
46302         range.collapse(true);
46303     
46304         //make the cursor there
46305         var sel = this.core.win.getSelection();
46306         sel.removeAllRanges();
46307         sel.addRange(range);
46308     
46309         return false;
46310          
46311     }
46312 };
46313      
46314 /**
46315  * @class Roo.htmleditor.Block
46316  * Base class for html editor blocks - do not use it directly .. extend it..
46317  * @cfg {DomElement} node The node to apply stuff to.
46318  * @cfg {String} friendly_name the name that appears in the context bar about this block
46319  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46320  
46321  * @constructor
46322  * Create a new Filter.
46323  * @param {Object} config Configuration options
46324  */
46325
46326 Roo.htmleditor.Block  = function(cfg)
46327 {
46328     // do nothing .. should not be called really.
46329 }
46330
46331 Roo.htmleditor.Block.factory = function(node)
46332 {
46333     
46334     var id = Roo.get(node).id;
46335     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46336         Roo.htmleditor.Block.cache[id].readElement();
46337         return Roo.htmleditor.Block.cache[id];
46338     }
46339     
46340     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46341     if (typeof(cls) == 'undefined') {
46342         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46343         return false;
46344     }
46345     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46346     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46347 };
46348 // question goes here... do we need to clear out this cache sometimes?
46349 // or show we make it relivant to the htmleditor.
46350 Roo.htmleditor.Block.cache = {};
46351
46352 Roo.htmleditor.Block.prototype = {
46353     
46354     node : false,
46355     
46356      // used by context menu
46357     friendly_name : 'Image with caption',
46358     
46359     context : false,
46360     /**
46361      * Update a node with values from this object
46362      * @param {DomElement} node
46363      */
46364     updateElement : function(node)
46365     {
46366         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46367     },
46368      /**
46369      * convert to plain HTML for calling insertAtCursor..
46370      */
46371     toHTML : function()
46372     {
46373         return Roo.DomHelper.markup(this.toObject());
46374     },
46375     /**
46376      * used by readEleemnt to extract data from a node
46377      * may need improving as it's pretty basic
46378      
46379      * @param {DomElement} node
46380      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46381      * @param {String} attribute (use html - for contents, or style for using next param as style)
46382      * @param {String} style the style property - eg. text-align
46383      */
46384     getVal : function(node, tag, attr, style)
46385     {
46386         var n = node;
46387         if (tag !== true && n.tagName != tag.toUpperCase()) {
46388             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46389             // but kiss for now.
46390             n = node.getElementsByTagName(tag).item(0);
46391         }
46392         if (attr == 'html') {
46393             return n.innerHTML;
46394         }
46395         if (attr == 'style') {
46396             return Roo.get(n).getStyle(style);
46397         }
46398         
46399         return Roo.get(n).attr(attr);
46400             
46401     },
46402     /**
46403      * create a DomHelper friendly object - for use with 
46404      * Roo.DomHelper.markup / overwrite / etc..
46405      * (override this)
46406      */
46407     toObject : function()
46408     {
46409         return {};
46410     },
46411       /**
46412      * Read a node that has a 'data-block' property - and extract the values from it.
46413      * @param {DomElement} node - the node
46414      */
46415     readElement : function(node)
46416     {
46417         
46418     } 
46419     
46420     
46421 };
46422
46423  
46424
46425 /**
46426  * @class Roo.htmleditor.BlockFigure
46427  * Block that has an image and a figcaption
46428  * @cfg {String} image_src the url for the image
46429  * @cfg {String} align (left|right) alignment for the block default left
46430  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46431  * @cfg {String} caption the text to appear below  (and in the alt tag)
46432  * @cfg {String|number} image_width the width of the image number or %?
46433  * @cfg {String|number} image_height the height of the image number or %?
46434  * 
46435  * @constructor
46436  * Create a new Filter.
46437  * @param {Object} config Configuration options
46438  */
46439
46440 Roo.htmleditor.BlockFigure = function(cfg)
46441 {
46442     if (cfg.node) {
46443         this.readElement(cfg.node);
46444         this.updateElement(cfg.node);
46445     }
46446     Roo.apply(this, cfg);
46447 }
46448 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46449  
46450     
46451     // setable values.
46452     image_src: '',
46453     
46454     align: 'left',
46455     caption : '',
46456     text_align: 'left',
46457     
46458     width : '46%',
46459     margin: '2%',
46460     
46461     // used by context menu
46462     friendly_name : 'Image with caption',
46463     
46464     context : { // ?? static really
46465         width : {
46466             title: "Width",
46467             width: 40
46468             // ?? number
46469         },
46470         margin : {
46471             title: "Margin",
46472             width: 40
46473             // ?? number
46474         },
46475         align: {
46476             title: "Align",
46477             opts : [[ "left"],[ "right"]],
46478             width : 80
46479             
46480         },
46481         text_align: {
46482             title: "Caption Align",
46483             opts : [ [ "left"],[ "right"],[ "center"]],
46484             width : 80
46485         },
46486         
46487        
46488         image_src : {
46489             title: "Src",
46490             width: 220
46491         }
46492     },
46493     /**
46494      * create a DomHelper friendly object - for use with
46495      * Roo.DomHelper.markup / overwrite / etc..
46496      */
46497     toObject : function()
46498     {
46499         var d = document.createElement('div');
46500         d.innerHTML = this.caption;
46501         
46502         return {
46503             tag: 'figure',
46504             'data-block' : 'Figure',
46505             contenteditable : 'false',
46506             style : {
46507                 display: 'table',
46508                 float :  this.align ,
46509                 width :  this.width,
46510                 margin:  this.margin
46511             },
46512             cn : [
46513                 {
46514                     tag : 'img',
46515                     src : this.image_src,
46516                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46517                     style: {
46518                         width: '100%'
46519                     }
46520                 },
46521                 {
46522                     tag: 'figcaption',
46523                     contenteditable : true,
46524                     style : {
46525                         'text-align': this.text_align
46526                     },
46527                     html : this.caption
46528                     
46529                 }
46530             ]
46531         };
46532     },
46533     
46534     readElement : function(node)
46535     {
46536         this.image_src = this.getVal(node, 'img', 'src');
46537         this.align = this.getVal(node, 'figure', 'style', 'float');
46538         this.caption = this.getVal(node, 'figcaption', 'html');
46539         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46540         this.width = this.getVal(node, 'figure', 'style', 'width');
46541         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46542         
46543     } 
46544     
46545   
46546    
46547      
46548     
46549     
46550     
46551     
46552 })
46553
46554 //<script type="text/javascript">
46555
46556 /*
46557  * Based  Ext JS Library 1.1.1
46558  * Copyright(c) 2006-2007, Ext JS, LLC.
46559  * LGPL
46560  *
46561  */
46562  
46563 /**
46564  * @class Roo.HtmlEditorCore
46565  * @extends Roo.Component
46566  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46567  *
46568  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46569  */
46570
46571 Roo.HtmlEditorCore = function(config){
46572     
46573     
46574     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46575     
46576     
46577     this.addEvents({
46578         /**
46579          * @event initialize
46580          * Fires when the editor is fully initialized (including the iframe)
46581          * @param {Roo.HtmlEditorCore} this
46582          */
46583         initialize: true,
46584         /**
46585          * @event activate
46586          * Fires when the editor is first receives the focus. Any insertion must wait
46587          * until after this event.
46588          * @param {Roo.HtmlEditorCore} this
46589          */
46590         activate: true,
46591          /**
46592          * @event beforesync
46593          * Fires before the textarea is updated with content from the editor iframe. Return false
46594          * to cancel the sync.
46595          * @param {Roo.HtmlEditorCore} this
46596          * @param {String} html
46597          */
46598         beforesync: true,
46599          /**
46600          * @event beforepush
46601          * Fires before the iframe editor is updated with content from the textarea. Return false
46602          * to cancel the push.
46603          * @param {Roo.HtmlEditorCore} this
46604          * @param {String} html
46605          */
46606         beforepush: true,
46607          /**
46608          * @event sync
46609          * Fires when the textarea is updated with content from the editor iframe.
46610          * @param {Roo.HtmlEditorCore} this
46611          * @param {String} html
46612          */
46613         sync: true,
46614          /**
46615          * @event push
46616          * Fires when the iframe editor is updated with content from the textarea.
46617          * @param {Roo.HtmlEditorCore} this
46618          * @param {String} html
46619          */
46620         push: true,
46621         
46622         /**
46623          * @event editorevent
46624          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46625          * @param {Roo.HtmlEditorCore} this
46626          */
46627         editorevent: true
46628         
46629     });
46630     
46631     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46632     
46633     // defaults : white / black...
46634     this.applyBlacklists();
46635     
46636     
46637     
46638 };
46639
46640
46641 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46642
46643
46644      /**
46645      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46646      */
46647     
46648     owner : false,
46649     
46650      /**
46651      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46652      *                        Roo.resizable.
46653      */
46654     resizable : false,
46655      /**
46656      * @cfg {Number} height (in pixels)
46657      */   
46658     height: 300,
46659    /**
46660      * @cfg {Number} width (in pixels)
46661      */   
46662     width: 500,
46663     
46664     /**
46665      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46666      * 
46667      */
46668     stylesheets: false,
46669     
46670     /**
46671      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46672      */
46673     allowComments: false,
46674     // id of frame..
46675     frameId: false,
46676     
46677     // private properties
46678     validationEvent : false,
46679     deferHeight: true,
46680     initialized : false,
46681     activated : false,
46682     sourceEditMode : false,
46683     onFocus : Roo.emptyFn,
46684     iframePad:3,
46685     hideMode:'offsets',
46686     
46687     clearUp: true,
46688     
46689     // blacklist + whitelisted elements..
46690     black: false,
46691     white: false,
46692      
46693     bodyCls : '',
46694
46695     
46696     undoManager : false,
46697     /**
46698      * Protected method that will not generally be called directly. It
46699      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46700      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46701      */
46702     getDocMarkup : function(){
46703         // body styles..
46704         var st = '';
46705         
46706         // inherit styels from page...?? 
46707         if (this.stylesheets === false) {
46708             
46709             Roo.get(document.head).select('style').each(function(node) {
46710                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46711             });
46712             
46713             Roo.get(document.head).select('link').each(function(node) { 
46714                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46715             });
46716             
46717         } else if (!this.stylesheets.length) {
46718                 // simple..
46719                 st = '<style type="text/css">' +
46720                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46721                    '</style>';
46722         } else {
46723             for (var i in this.stylesheets) {
46724                 if (typeof(this.stylesheets[i]) != 'string') {
46725                     continue;
46726                 }
46727                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46728             }
46729             
46730         }
46731         
46732         st +=  '<style type="text/css">' +
46733             'IMG { cursor: pointer } ' +
46734         '</style>';
46735
46736         var cls = 'roo-htmleditor-body';
46737         
46738         if(this.bodyCls.length){
46739             cls += ' ' + this.bodyCls;
46740         }
46741         
46742         return '<html><head>' + st  +
46743             //<style type="text/css">' +
46744             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46745             //'</style>' +
46746             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46747     },
46748
46749     // private
46750     onRender : function(ct, position)
46751     {
46752         var _t = this;
46753         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46754         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46755         
46756         
46757         this.el.dom.style.border = '0 none';
46758         this.el.dom.setAttribute('tabIndex', -1);
46759         this.el.addClass('x-hidden hide');
46760         
46761         
46762         
46763         if(Roo.isIE){ // fix IE 1px bogus margin
46764             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46765         }
46766        
46767         
46768         this.frameId = Roo.id();
46769         
46770          
46771         
46772         var iframe = this.owner.wrap.createChild({
46773             tag: 'iframe',
46774             cls: 'form-control', // bootstrap..
46775             id: this.frameId,
46776             name: this.frameId,
46777             frameBorder : 'no',
46778             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46779         }, this.el
46780         );
46781         
46782         
46783         this.iframe = iframe.dom;
46784
46785         this.assignDocWin();
46786         
46787         this.doc.designMode = 'on';
46788        
46789         this.doc.open();
46790         this.doc.write(this.getDocMarkup());
46791         this.doc.close();
46792
46793         
46794         var task = { // must defer to wait for browser to be ready
46795             run : function(){
46796                 //console.log("run task?" + this.doc.readyState);
46797                 this.assignDocWin();
46798                 if(this.doc.body || this.doc.readyState == 'complete'){
46799                     try {
46800                         this.doc.designMode="on";
46801                         
46802                     } catch (e) {
46803                         return;
46804                     }
46805                     Roo.TaskMgr.stop(task);
46806                     this.initEditor.defer(10, this);
46807                 }
46808             },
46809             interval : 10,
46810             duration: 10000,
46811             scope: this
46812         };
46813         Roo.TaskMgr.start(task);
46814
46815     },
46816
46817     // private
46818     onResize : function(w, h)
46819     {
46820          Roo.log('resize: ' +w + ',' + h );
46821         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46822         if(!this.iframe){
46823             return;
46824         }
46825         if(typeof w == 'number'){
46826             
46827             this.iframe.style.width = w + 'px';
46828         }
46829         if(typeof h == 'number'){
46830             
46831             this.iframe.style.height = h + 'px';
46832             if(this.doc){
46833                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46834             }
46835         }
46836         
46837     },
46838
46839     /**
46840      * Toggles the editor between standard and source edit mode.
46841      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46842      */
46843     toggleSourceEdit : function(sourceEditMode){
46844         
46845         this.sourceEditMode = sourceEditMode === true;
46846         
46847         if(this.sourceEditMode){
46848  
46849             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46850             
46851         }else{
46852             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46853             //this.iframe.className = '';
46854             this.deferFocus();
46855         }
46856         //this.setSize(this.owner.wrap.getSize());
46857         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46858     },
46859
46860     
46861   
46862
46863     /**
46864      * Protected method that will not generally be called directly. If you need/want
46865      * custom HTML cleanup, this is the method you should override.
46866      * @param {String} html The HTML to be cleaned
46867      * return {String} The cleaned HTML
46868      */
46869     cleanHtml : function(html){
46870         html = String(html);
46871         if(html.length > 5){
46872             if(Roo.isSafari){ // strip safari nonsense
46873                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46874             }
46875         }
46876         if(html == '&nbsp;'){
46877             html = '';
46878         }
46879         return html;
46880     },
46881
46882     /**
46883      * HTML Editor -> Textarea
46884      * Protected method that will not generally be called directly. Syncs the contents
46885      * of the editor iframe with the textarea.
46886      */
46887     syncValue : function()
46888     {
46889         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46890         if(this.initialized){
46891             
46892             this.undoManager.addEvent();
46893
46894             
46895             var bd = (this.doc.body || this.doc.documentElement);
46896             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46897             
46898             // not sure if this is really the place for this
46899             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46900             // this has to update attributes that get duped.. like alt and caption..
46901             
46902             
46903             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46904             //     Roo.htmleditor.Block.factory(e);
46905             //},this);
46906             
46907             
46908             var div = document.createElement('div');
46909             div.innerHTML = bd.innerHTML;
46910             // remove content editable. (blocks)
46911             
46912            
46913             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46914             //?? tidy?
46915             var html = div.innerHTML;
46916             if(Roo.isSafari){
46917                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46918                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46919                 if(m && m[1]){
46920                     html = '<div style="'+m[0]+'">' + html + '</div>';
46921                 }
46922             }
46923             html = this.cleanHtml(html);
46924             // fix up the special chars.. normaly like back quotes in word...
46925             // however we do not want to do this with chinese..
46926             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46927                 
46928                 var cc = match.charCodeAt();
46929
46930                 // Get the character value, handling surrogate pairs
46931                 if (match.length == 2) {
46932                     // It's a surrogate pair, calculate the Unicode code point
46933                     var high = match.charCodeAt(0) - 0xD800;
46934                     var low  = match.charCodeAt(1) - 0xDC00;
46935                     cc = (high * 0x400) + low + 0x10000;
46936                 }  else if (
46937                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46938                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46939                     (cc >= 0xf900 && cc < 0xfb00 )
46940                 ) {
46941                         return match;
46942                 }  
46943          
46944                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46945                 return "&#" + cc + ";";
46946                 
46947                 
46948             });
46949             
46950             
46951              
46952             if(this.owner.fireEvent('beforesync', this, html) !== false){
46953                 this.el.dom.value = html;
46954                 this.owner.fireEvent('sync', this, html);
46955             }
46956         }
46957     },
46958
46959     /**
46960      * TEXTAREA -> EDITABLE
46961      * Protected method that will not generally be called directly. Pushes the value of the textarea
46962      * into the iframe editor.
46963      */
46964     pushValue : function()
46965     {
46966         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46967         if(this.initialized){
46968             var v = this.el.dom.value.trim();
46969             
46970             
46971             if(this.owner.fireEvent('beforepush', this, v) !== false){
46972                 var d = (this.doc.body || this.doc.documentElement);
46973                 d.innerHTML = v;
46974                  
46975                 this.el.dom.value = d.innerHTML;
46976                 this.owner.fireEvent('push', this, v);
46977             }
46978             
46979             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46980                 
46981                 Roo.htmleditor.Block.factory(e);
46982                 
46983             },this);
46984             var lc = this.doc.body.lastChild;
46985             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46986                 // add an extra line at the end.
46987                 this.doc.body.appendChild(this.doc.createElement('br'));
46988             }
46989             
46990             
46991         }
46992     },
46993
46994     // private
46995     deferFocus : function(){
46996         this.focus.defer(10, this);
46997     },
46998
46999     // doc'ed in Field
47000     focus : function(){
47001         if(this.win && !this.sourceEditMode){
47002             this.win.focus();
47003         }else{
47004             this.el.focus();
47005         }
47006     },
47007     
47008     assignDocWin: function()
47009     {
47010         var iframe = this.iframe;
47011         
47012          if(Roo.isIE){
47013             this.doc = iframe.contentWindow.document;
47014             this.win = iframe.contentWindow;
47015         } else {
47016 //            if (!Roo.get(this.frameId)) {
47017 //                return;
47018 //            }
47019 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47020 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47021             
47022             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47023                 return;
47024             }
47025             
47026             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47027             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47028         }
47029     },
47030     
47031     // private
47032     initEditor : function(){
47033         //console.log("INIT EDITOR");
47034         this.assignDocWin();
47035         
47036         
47037         
47038         this.doc.designMode="on";
47039         this.doc.open();
47040         this.doc.write(this.getDocMarkup());
47041         this.doc.close();
47042         
47043         var dbody = (this.doc.body || this.doc.documentElement);
47044         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47045         // this copies styles from the containing element into thsi one..
47046         // not sure why we need all of this..
47047         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47048         
47049         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47050         //ss['background-attachment'] = 'fixed'; // w3c
47051         dbody.bgProperties = 'fixed'; // ie
47052         //Roo.DomHelper.applyStyles(dbody, ss);
47053         Roo.EventManager.on(this.doc, {
47054             //'mousedown': this.onEditorEvent,
47055             'mouseup': this.onEditorEvent,
47056             'dblclick': this.onEditorEvent,
47057             'click': this.onEditorEvent,
47058             'keyup': this.onEditorEvent,
47059             
47060             buffer:100,
47061             scope: this
47062         });
47063         Roo.EventManager.on(this.doc, {
47064             'paste': this.onPasteEvent,
47065             scope : this
47066         });
47067         if(Roo.isGecko){
47068             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47069         }
47070         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47071             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47072         }
47073         this.initialized = true;
47074
47075         
47076         // initialize special key events - enter
47077         new Roo.htmleditor.KeyEnter({core : this});
47078         
47079          
47080         
47081         this.owner.fireEvent('initialize', this);
47082         this.pushValue();
47083     },
47084     
47085     onPasteEvent : function(e,v)
47086     {
47087         // I think we better assume paste is going to be a dirty load of rubish from word..
47088         
47089         // even pasting into a 'email version' of this widget will have to clean up that mess.
47090         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47091         
47092         // check what type of paste - if it's an image, then handle it differently.
47093         if (cd.files.length > 0) {
47094             // pasting images?
47095             var urlAPI = (window.createObjectURL && window) || 
47096                 (window.URL && URL.revokeObjectURL && URL) || 
47097                 (window.webkitURL && webkitURL);
47098     
47099             var url = urlAPI.createObjectURL( cd.files[0]);
47100             this.insertAtCursor('<img src=" + url + ">');
47101             return false;
47102         }
47103         
47104         var html = cd.getData('text/html'); // clipboard event
47105         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47106         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47107         Roo.log(images);
47108         //Roo.log(imgs);
47109         // fixme..
47110         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47111                        .map(function(g) { return g.toDataURL(); });
47112         
47113         
47114         html = this.cleanWordChars(html);
47115         
47116         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47117         
47118         if (images.length > 0) {
47119             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47120                 img.setAttribute('src', images[i]);
47121             });
47122         }
47123         
47124       
47125         new Roo.htmleditor.FilterStyleToTag({ node : d });
47126         new Roo.htmleditor.FilterAttributes({
47127             node : d,
47128             attrib_white : ['href', 'src', 'name', 'align'],
47129             attrib_clean : ['href', 'src' ] 
47130         });
47131         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47132         // should be fonts..
47133         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47134         new Roo.htmleditor.FilterParagraph({ node : d });
47135         new Roo.htmleditor.FilterSpan({ node : d });
47136         new Roo.htmleditor.FilterLongBr({ node : d });
47137         
47138         
47139         
47140         this.insertAtCursor(d.innerHTML);
47141         
47142         e.preventDefault();
47143         return false;
47144         // default behaveiour should be our local cleanup paste? (optional?)
47145         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47146         //this.owner.fireEvent('paste', e, v);
47147     },
47148     // private
47149     onDestroy : function(){
47150         
47151         
47152         
47153         if(this.rendered){
47154             
47155             //for (var i =0; i < this.toolbars.length;i++) {
47156             //    // fixme - ask toolbars for heights?
47157             //    this.toolbars[i].onDestroy();
47158            // }
47159             
47160             //this.wrap.dom.innerHTML = '';
47161             //this.wrap.remove();
47162         }
47163     },
47164
47165     // private
47166     onFirstFocus : function(){
47167         
47168         this.assignDocWin();
47169         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47170         
47171         this.activated = true;
47172          
47173     
47174         if(Roo.isGecko){ // prevent silly gecko errors
47175             this.win.focus();
47176             var s = this.win.getSelection();
47177             if(!s.focusNode || s.focusNode.nodeType != 3){
47178                 var r = s.getRangeAt(0);
47179                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47180                 r.collapse(true);
47181                 this.deferFocus();
47182             }
47183             try{
47184                 this.execCmd('useCSS', true);
47185                 this.execCmd('styleWithCSS', false);
47186             }catch(e){}
47187         }
47188         this.owner.fireEvent('activate', this);
47189     },
47190
47191     // private
47192     adjustFont: function(btn){
47193         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47194         //if(Roo.isSafari){ // safari
47195         //    adjust *= 2;
47196        // }
47197         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47198         if(Roo.isSafari){ // safari
47199             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47200             v =  (v < 10) ? 10 : v;
47201             v =  (v > 48) ? 48 : v;
47202             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47203             
47204         }
47205         
47206         
47207         v = Math.max(1, v+adjust);
47208         
47209         this.execCmd('FontSize', v  );
47210     },
47211
47212     onEditorEvent : function(e)
47213     {
47214         this.owner.fireEvent('editorevent', this, e);
47215       //  this.updateToolbar();
47216         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47217     },
47218
47219     insertTag : function(tg)
47220     {
47221         // could be a bit smarter... -> wrap the current selected tRoo..
47222         if (tg.toLowerCase() == 'span' ||
47223             tg.toLowerCase() == 'code' ||
47224             tg.toLowerCase() == 'sup' ||
47225             tg.toLowerCase() == 'sub' 
47226             ) {
47227             
47228             range = this.createRange(this.getSelection());
47229             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47230             wrappingNode.appendChild(range.extractContents());
47231             range.insertNode(wrappingNode);
47232
47233             return;
47234             
47235             
47236             
47237         }
47238         this.execCmd("formatblock",   tg);
47239         this.undoManager.addEvent(); 
47240     },
47241     
47242     insertText : function(txt)
47243     {
47244         
47245         
47246         var range = this.createRange();
47247         range.deleteContents();
47248                //alert(Sender.getAttribute('label'));
47249                
47250         range.insertNode(this.doc.createTextNode(txt));
47251         this.undoManager.addEvent();
47252     } ,
47253     
47254      
47255
47256     /**
47257      * Executes a Midas editor command on the editor document and performs necessary focus and
47258      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47259      * @param {String} cmd The Midas command
47260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47261      */
47262     relayCmd : function(cmd, value){
47263         this.win.focus();
47264         this.execCmd(cmd, value);
47265         this.owner.fireEvent('editorevent', this);
47266         //this.updateToolbar();
47267         this.owner.deferFocus();
47268     },
47269
47270     /**
47271      * Executes a Midas editor command directly on the editor document.
47272      * For visual commands, you should use {@link #relayCmd} instead.
47273      * <b>This should only be called after the editor is initialized.</b>
47274      * @param {String} cmd The Midas command
47275      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47276      */
47277     execCmd : function(cmd, value){
47278         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47279         this.syncValue();
47280     },
47281  
47282  
47283    
47284     /**
47285      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47286      * to insert tRoo.
47287      * @param {String} text | dom node.. 
47288      */
47289     insertAtCursor : function(text)
47290     {
47291         
47292         if(!this.activated){
47293             return;
47294         }
47295          
47296         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47297             this.win.focus();
47298             
47299             
47300             // from jquery ui (MIT licenced)
47301             var range, node;
47302             var win = this.win;
47303             
47304             if (win.getSelection && win.getSelection().getRangeAt) {
47305                 
47306                 // delete the existing?
47307                 
47308                 this.createRange(this.getSelection()).deleteContents();
47309                 range = win.getSelection().getRangeAt(0);
47310                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47311                 range.insertNode(node);
47312                 range = range.cloneRange();
47313                 range.collapse(false);
47314                  
47315                 win.getSelection().removeAllRanges();
47316                 win.getSelection().addRange(range);
47317                 
47318                 
47319                 
47320             } else if (win.document.selection && win.document.selection.createRange) {
47321                 // no firefox support
47322                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47323                 win.document.selection.createRange().pasteHTML(txt);
47324             
47325             } else {
47326                 // no firefox support
47327                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47328                 this.execCmd('InsertHTML', txt);
47329             } 
47330             this.syncValue();
47331             
47332             this.deferFocus();
47333         }
47334     },
47335  // private
47336     mozKeyPress : function(e){
47337         if(e.ctrlKey){
47338             var c = e.getCharCode(), cmd;
47339           
47340             if(c > 0){
47341                 c = String.fromCharCode(c).toLowerCase();
47342                 switch(c){
47343                     case 'b':
47344                         cmd = 'bold';
47345                         break;
47346                     case 'i':
47347                         cmd = 'italic';
47348                         break;
47349                     
47350                     case 'u':
47351                         cmd = 'underline';
47352                         break;
47353                     
47354                     //case 'v':
47355                       //  this.cleanUpPaste.defer(100, this);
47356                       //  return;
47357                         
47358                 }
47359                 if(cmd){
47360                     this.win.focus();
47361                     this.execCmd(cmd);
47362                     this.deferFocus();
47363                     e.preventDefault();
47364                 }
47365                 
47366             }
47367         }
47368     },
47369
47370     // private
47371     fixKeys : function(){ // load time branching for fastest keydown performance
47372         if(Roo.isIE){
47373             return function(e){
47374                 var k = e.getKey(), r;
47375                 if(k == e.TAB){
47376                     e.stopEvent();
47377                     r = this.doc.selection.createRange();
47378                     if(r){
47379                         r.collapse(true);
47380                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47381                         this.deferFocus();
47382                     }
47383                     return;
47384                 }
47385                 
47386                 if(k == e.ENTER){
47387                     r = this.doc.selection.createRange();
47388                     if(r){
47389                         var target = r.parentElement();
47390                         if(!target || target.tagName.toLowerCase() != 'li'){
47391                             e.stopEvent();
47392                             r.pasteHTML('<br/>');
47393                             r.collapse(false);
47394                             r.select();
47395                         }
47396                     }
47397                 }
47398                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47399                 //    this.cleanUpPaste.defer(100, this);
47400                 //    return;
47401                 //}
47402                 
47403                 
47404             };
47405         }else if(Roo.isOpera){
47406             return function(e){
47407                 var k = e.getKey();
47408                 if(k == e.TAB){
47409                     e.stopEvent();
47410                     this.win.focus();
47411                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47412                     this.deferFocus();
47413                 }
47414                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47415                 //    this.cleanUpPaste.defer(100, this);
47416                  //   return;
47417                 //}
47418                 
47419             };
47420         }else if(Roo.isSafari){
47421             return function(e){
47422                 var k = e.getKey();
47423                 
47424                 if(k == e.TAB){
47425                     e.stopEvent();
47426                     this.execCmd('InsertText','\t');
47427                     this.deferFocus();
47428                     return;
47429                 }
47430                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47431                  //   this.cleanUpPaste.defer(100, this);
47432                  //   return;
47433                // }
47434                 
47435              };
47436         }
47437     }(),
47438     
47439     getAllAncestors: function()
47440     {
47441         var p = this.getSelectedNode();
47442         var a = [];
47443         if (!p) {
47444             a.push(p); // push blank onto stack..
47445             p = this.getParentElement();
47446         }
47447         
47448         
47449         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47450             a.push(p);
47451             p = p.parentNode;
47452         }
47453         a.push(this.doc.body);
47454         return a;
47455     },
47456     lastSel : false,
47457     lastSelNode : false,
47458     
47459     
47460     getSelection : function() 
47461     {
47462         this.assignDocWin();
47463         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47464     },
47465     /**
47466      * Select a dom node
47467      * @param {DomElement} node the node to select
47468      */
47469     selectNode : function(node)
47470     {
47471         var nodeRange = node.ownerDocument.createRange();
47472         try {
47473             nodeRange.selectNode(node);
47474         } catch (e) {
47475             nodeRange.selectNodeContents(node);
47476         }
47477         //nodeRange.collapse(true);
47478         var s = this.win.getSelection();
47479         s.removeAllRanges();
47480         s.addRange(nodeRange);
47481     },
47482     
47483     getSelectedNode: function() 
47484     {
47485         // this may only work on Gecko!!!
47486         
47487         // should we cache this!!!!
47488         
47489         
47490         
47491          
47492         var range = this.createRange(this.getSelection()).cloneRange();
47493         
47494         if (Roo.isIE) {
47495             var parent = range.parentElement();
47496             while (true) {
47497                 var testRange = range.duplicate();
47498                 testRange.moveToElementText(parent);
47499                 if (testRange.inRange(range)) {
47500                     break;
47501                 }
47502                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47503                     break;
47504                 }
47505                 parent = parent.parentElement;
47506             }
47507             return parent;
47508         }
47509         
47510         // is ancestor a text element.
47511         var ac =  range.commonAncestorContainer;
47512         if (ac.nodeType == 3) {
47513             ac = ac.parentNode;
47514         }
47515         
47516         var ar = ac.childNodes;
47517          
47518         var nodes = [];
47519         var other_nodes = [];
47520         var has_other_nodes = false;
47521         for (var i=0;i<ar.length;i++) {
47522             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47523                 continue;
47524             }
47525             // fullly contained node.
47526             
47527             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47528                 nodes.push(ar[i]);
47529                 continue;
47530             }
47531             
47532             // probably selected..
47533             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47534                 other_nodes.push(ar[i]);
47535                 continue;
47536             }
47537             // outer..
47538             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47539                 continue;
47540             }
47541             
47542             
47543             has_other_nodes = true;
47544         }
47545         if (!nodes.length && other_nodes.length) {
47546             nodes= other_nodes;
47547         }
47548         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47549             return false;
47550         }
47551         
47552         return nodes[0];
47553     },
47554     createRange: function(sel)
47555     {
47556         // this has strange effects when using with 
47557         // top toolbar - not sure if it's a great idea.
47558         //this.editor.contentWindow.focus();
47559         if (typeof sel != "undefined") {
47560             try {
47561                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47562             } catch(e) {
47563                 return this.doc.createRange();
47564             }
47565         } else {
47566             return this.doc.createRange();
47567         }
47568     },
47569     getParentElement: function()
47570     {
47571         
47572         this.assignDocWin();
47573         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47574         
47575         var range = this.createRange(sel);
47576          
47577         try {
47578             var p = range.commonAncestorContainer;
47579             while (p.nodeType == 3) { // text node
47580                 p = p.parentNode;
47581             }
47582             return p;
47583         } catch (e) {
47584             return null;
47585         }
47586     
47587     },
47588     /***
47589      *
47590      * Range intersection.. the hard stuff...
47591      *  '-1' = before
47592      *  '0' = hits..
47593      *  '1' = after.
47594      *         [ -- selected range --- ]
47595      *   [fail]                        [fail]
47596      *
47597      *    basically..
47598      *      if end is before start or  hits it. fail.
47599      *      if start is after end or hits it fail.
47600      *
47601      *   if either hits (but other is outside. - then it's not 
47602      *   
47603      *    
47604      **/
47605     
47606     
47607     // @see http://www.thismuchiknow.co.uk/?p=64.
47608     rangeIntersectsNode : function(range, node)
47609     {
47610         var nodeRange = node.ownerDocument.createRange();
47611         try {
47612             nodeRange.selectNode(node);
47613         } catch (e) {
47614             nodeRange.selectNodeContents(node);
47615         }
47616     
47617         var rangeStartRange = range.cloneRange();
47618         rangeStartRange.collapse(true);
47619     
47620         var rangeEndRange = range.cloneRange();
47621         rangeEndRange.collapse(false);
47622     
47623         var nodeStartRange = nodeRange.cloneRange();
47624         nodeStartRange.collapse(true);
47625     
47626         var nodeEndRange = nodeRange.cloneRange();
47627         nodeEndRange.collapse(false);
47628     
47629         return rangeStartRange.compareBoundaryPoints(
47630                  Range.START_TO_START, nodeEndRange) == -1 &&
47631                rangeEndRange.compareBoundaryPoints(
47632                  Range.START_TO_START, nodeStartRange) == 1;
47633         
47634          
47635     },
47636     rangeCompareNode : function(range, node)
47637     {
47638         var nodeRange = node.ownerDocument.createRange();
47639         try {
47640             nodeRange.selectNode(node);
47641         } catch (e) {
47642             nodeRange.selectNodeContents(node);
47643         }
47644         
47645         
47646         range.collapse(true);
47647     
47648         nodeRange.collapse(true);
47649      
47650         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47651         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47652          
47653         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47654         
47655         var nodeIsBefore   =  ss == 1;
47656         var nodeIsAfter    = ee == -1;
47657         
47658         if (nodeIsBefore && nodeIsAfter) {
47659             return 0; // outer
47660         }
47661         if (!nodeIsBefore && nodeIsAfter) {
47662             return 1; //right trailed.
47663         }
47664         
47665         if (nodeIsBefore && !nodeIsAfter) {
47666             return 2;  // left trailed.
47667         }
47668         // fully contined.
47669         return 3;
47670     },
47671  
47672     cleanWordChars : function(input) {// change the chars to hex code
47673         
47674        var swapCodes  = [ 
47675             [    8211, "&#8211;" ], 
47676             [    8212, "&#8212;" ], 
47677             [    8216,  "'" ],  
47678             [    8217, "'" ],  
47679             [    8220, '"' ],  
47680             [    8221, '"' ],  
47681             [    8226, "*" ],  
47682             [    8230, "..." ]
47683         ]; 
47684         var output = input;
47685         Roo.each(swapCodes, function(sw) { 
47686             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47687             
47688             output = output.replace(swapper, sw[1]);
47689         });
47690         
47691         return output;
47692     },
47693     
47694      
47695     
47696         
47697     
47698     cleanUpChild : function (node)
47699     {
47700         
47701         new Roo.htmleditor.FilterComment({node : node});
47702         new Roo.htmleditor.FilterAttributes({
47703                 node : node,
47704                 attrib_black : this.ablack,
47705                 attrib_clean : this.aclean,
47706                 style_white : this.cwhite,
47707                 style_black : this.cblack
47708         });
47709         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47710         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47711          
47712         
47713     },
47714     
47715     /**
47716      * Clean up MS wordisms...
47717      * @deprecated - use filter directly
47718      */
47719     cleanWord : function(node)
47720     {
47721         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47722         
47723     },
47724    
47725     
47726     /**
47727
47728      * @deprecated - use filters
47729      */
47730     cleanTableWidths : function(node)
47731     {
47732         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47733         
47734  
47735     },
47736     
47737      
47738         
47739     applyBlacklists : function()
47740     {
47741         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47742         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47743         
47744         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47745         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47746         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47747         
47748         this.white = [];
47749         this.black = [];
47750         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47751             if (b.indexOf(tag) > -1) {
47752                 return;
47753             }
47754             this.white.push(tag);
47755             
47756         }, this);
47757         
47758         Roo.each(w, function(tag) {
47759             if (b.indexOf(tag) > -1) {
47760                 return;
47761             }
47762             if (this.white.indexOf(tag) > -1) {
47763                 return;
47764             }
47765             this.white.push(tag);
47766             
47767         }, this);
47768         
47769         
47770         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47771             if (w.indexOf(tag) > -1) {
47772                 return;
47773             }
47774             this.black.push(tag);
47775             
47776         }, this);
47777         
47778         Roo.each(b, function(tag) {
47779             if (w.indexOf(tag) > -1) {
47780                 return;
47781             }
47782             if (this.black.indexOf(tag) > -1) {
47783                 return;
47784             }
47785             this.black.push(tag);
47786             
47787         }, this);
47788         
47789         
47790         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47791         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47792         
47793         this.cwhite = [];
47794         this.cblack = [];
47795         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47796             if (b.indexOf(tag) > -1) {
47797                 return;
47798             }
47799             this.cwhite.push(tag);
47800             
47801         }, this);
47802         
47803         Roo.each(w, function(tag) {
47804             if (b.indexOf(tag) > -1) {
47805                 return;
47806             }
47807             if (this.cwhite.indexOf(tag) > -1) {
47808                 return;
47809             }
47810             this.cwhite.push(tag);
47811             
47812         }, this);
47813         
47814         
47815         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47816             if (w.indexOf(tag) > -1) {
47817                 return;
47818             }
47819             this.cblack.push(tag);
47820             
47821         }, this);
47822         
47823         Roo.each(b, function(tag) {
47824             if (w.indexOf(tag) > -1) {
47825                 return;
47826             }
47827             if (this.cblack.indexOf(tag) > -1) {
47828                 return;
47829             }
47830             this.cblack.push(tag);
47831             
47832         }, this);
47833     },
47834     
47835     setStylesheets : function(stylesheets)
47836     {
47837         if(typeof(stylesheets) == 'string'){
47838             Roo.get(this.iframe.contentDocument.head).createChild({
47839                 tag : 'link',
47840                 rel : 'stylesheet',
47841                 type : 'text/css',
47842                 href : stylesheets
47843             });
47844             
47845             return;
47846         }
47847         var _this = this;
47848      
47849         Roo.each(stylesheets, function(s) {
47850             if(!s.length){
47851                 return;
47852             }
47853             
47854             Roo.get(_this.iframe.contentDocument.head).createChild({
47855                 tag : 'link',
47856                 rel : 'stylesheet',
47857                 type : 'text/css',
47858                 href : s
47859             });
47860         });
47861
47862         
47863     },
47864     
47865     removeStylesheets : function()
47866     {
47867         var _this = this;
47868         
47869         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47870             s.remove();
47871         });
47872     },
47873     
47874     setStyle : function(style)
47875     {
47876         Roo.get(this.iframe.contentDocument.head).createChild({
47877             tag : 'style',
47878             type : 'text/css',
47879             html : style
47880         });
47881
47882         return;
47883     }
47884     
47885     // hide stuff that is not compatible
47886     /**
47887      * @event blur
47888      * @hide
47889      */
47890     /**
47891      * @event change
47892      * @hide
47893      */
47894     /**
47895      * @event focus
47896      * @hide
47897      */
47898     /**
47899      * @event specialkey
47900      * @hide
47901      */
47902     /**
47903      * @cfg {String} fieldClass @hide
47904      */
47905     /**
47906      * @cfg {String} focusClass @hide
47907      */
47908     /**
47909      * @cfg {String} autoCreate @hide
47910      */
47911     /**
47912      * @cfg {String} inputType @hide
47913      */
47914     /**
47915      * @cfg {String} invalidClass @hide
47916      */
47917     /**
47918      * @cfg {String} invalidText @hide
47919      */
47920     /**
47921      * @cfg {String} msgFx @hide
47922      */
47923     /**
47924      * @cfg {String} validateOnBlur @hide
47925      */
47926 });
47927
47928 Roo.HtmlEditorCore.white = [
47929         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47930         
47931        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47932        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47933        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47934        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47935        'TABLE',   'UL',         'XMP', 
47936        
47937        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47938       'THEAD',   'TR', 
47939      
47940       'DIR', 'MENU', 'OL', 'UL', 'DL',
47941        
47942       'EMBED',  'OBJECT'
47943 ];
47944
47945
47946 Roo.HtmlEditorCore.black = [
47947     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47948         'APPLET', // 
47949         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47950         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47951         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47952         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47953         //'FONT' // CLEAN LATER..
47954         'COLGROUP', 'COL'  // messy tables.
47955         
47956 ];
47957 Roo.HtmlEditorCore.clean = [ // ?? needed???
47958      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47959 ];
47960 Roo.HtmlEditorCore.tag_remove = [
47961     'FONT', 'TBODY'  
47962 ];
47963 // attributes..
47964
47965 Roo.HtmlEditorCore.ablack = [
47966     'on'
47967 ];
47968     
47969 Roo.HtmlEditorCore.aclean = [ 
47970     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47971 ];
47972
47973 // protocols..
47974 Roo.HtmlEditorCore.pwhite= [
47975         'http',  'https',  'mailto'
47976 ];
47977
47978 // white listed style attributes.
47979 Roo.HtmlEditorCore.cwhite= [
47980       //  'text-align', /// default is to allow most things..
47981       
47982          
47983 //        'font-size'//??
47984 ];
47985
47986 // black listed style attributes.
47987 Roo.HtmlEditorCore.cblack= [
47988       //  'font-size' -- this can be set by the project 
47989 ];
47990
47991
47992
47993
47994     //<script type="text/javascript">
47995
47996 /*
47997  * Ext JS Library 1.1.1
47998  * Copyright(c) 2006-2007, Ext JS, LLC.
47999  * Licence LGPL
48000  * 
48001  */
48002  
48003  
48004 Roo.form.HtmlEditor = function(config){
48005     
48006     
48007     
48008     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48009     
48010     if (!this.toolbars) {
48011         this.toolbars = [];
48012     }
48013     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48014     
48015     
48016 };
48017
48018 /**
48019  * @class Roo.form.HtmlEditor
48020  * @extends Roo.form.Field
48021  * Provides a lightweight HTML Editor component.
48022  *
48023  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48024  * 
48025  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48026  * supported by this editor.</b><br/><br/>
48027  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48028  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48029  */
48030 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48031     /**
48032      * @cfg {Boolean} clearUp
48033      */
48034     clearUp : true,
48035       /**
48036      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48037      */
48038     toolbars : false,
48039    
48040      /**
48041      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48042      *                        Roo.resizable.
48043      */
48044     resizable : false,
48045      /**
48046      * @cfg {Number} height (in pixels)
48047      */   
48048     height: 300,
48049    /**
48050      * @cfg {Number} width (in pixels)
48051      */   
48052     width: 500,
48053     
48054     /**
48055      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48056      * 
48057      */
48058     stylesheets: false,
48059     
48060     
48061      /**
48062      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48063      * 
48064      */
48065     cblack: false,
48066     /**
48067      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48068      * 
48069      */
48070     cwhite: false,
48071     
48072      /**
48073      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48074      * 
48075      */
48076     black: false,
48077     /**
48078      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48079      * 
48080      */
48081     white: false,
48082     /**
48083      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48084      */
48085     allowComments: false,
48086     /**
48087      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48088      */
48089     
48090     
48091      bodyCls : '',
48092     
48093     // id of frame..
48094     frameId: false,
48095     
48096     // private properties
48097     validationEvent : false,
48098     deferHeight: true,
48099     initialized : false,
48100     activated : false,
48101     
48102     onFocus : Roo.emptyFn,
48103     iframePad:3,
48104     hideMode:'offsets',
48105     
48106     actionMode : 'container', // defaults to hiding it...
48107     
48108     defaultAutoCreate : { // modified by initCompnoent..
48109         tag: "textarea",
48110         style:"width:500px;height:300px;",
48111         autocomplete: "new-password"
48112     },
48113
48114     // private
48115     initComponent : function(){
48116         this.addEvents({
48117             /**
48118              * @event initialize
48119              * Fires when the editor is fully initialized (including the iframe)
48120              * @param {HtmlEditor} this
48121              */
48122             initialize: true,
48123             /**
48124              * @event activate
48125              * Fires when the editor is first receives the focus. Any insertion must wait
48126              * until after this event.
48127              * @param {HtmlEditor} this
48128              */
48129             activate: true,
48130              /**
48131              * @event beforesync
48132              * Fires before the textarea is updated with content from the editor iframe. Return false
48133              * to cancel the sync.
48134              * @param {HtmlEditor} this
48135              * @param {String} html
48136              */
48137             beforesync: true,
48138              /**
48139              * @event beforepush
48140              * Fires before the iframe editor is updated with content from the textarea. Return false
48141              * to cancel the push.
48142              * @param {HtmlEditor} this
48143              * @param {String} html
48144              */
48145             beforepush: true,
48146              /**
48147              * @event sync
48148              * Fires when the textarea is updated with content from the editor iframe.
48149              * @param {HtmlEditor} this
48150              * @param {String} html
48151              */
48152             sync: true,
48153              /**
48154              * @event push
48155              * Fires when the iframe editor is updated with content from the textarea.
48156              * @param {HtmlEditor} this
48157              * @param {String} html
48158              */
48159             push: true,
48160              /**
48161              * @event editmodechange
48162              * Fires when the editor switches edit modes
48163              * @param {HtmlEditor} this
48164              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48165              */
48166             editmodechange: true,
48167             /**
48168              * @event editorevent
48169              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48170              * @param {HtmlEditor} this
48171              */
48172             editorevent: true,
48173             /**
48174              * @event firstfocus
48175              * Fires when on first focus - needed by toolbars..
48176              * @param {HtmlEditor} this
48177              */
48178             firstfocus: true,
48179             /**
48180              * @event autosave
48181              * Auto save the htmlEditor value as a file into Events
48182              * @param {HtmlEditor} this
48183              */
48184             autosave: true,
48185             /**
48186              * @event savedpreview
48187              * preview the saved version of htmlEditor
48188              * @param {HtmlEditor} this
48189              */
48190             savedpreview: true,
48191             
48192             /**
48193             * @event stylesheetsclick
48194             * Fires when press the Sytlesheets button
48195             * @param {Roo.HtmlEditorCore} this
48196             */
48197             stylesheetsclick: true,
48198             /**
48199             * @event paste
48200             * Fires when press user pastes into the editor
48201             * @param {Roo.HtmlEditorCore} this
48202             */
48203             paste: true 
48204         });
48205         this.defaultAutoCreate =  {
48206             tag: "textarea",
48207             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48208             autocomplete: "new-password"
48209         };
48210     },
48211
48212     /**
48213      * Protected method that will not generally be called directly. It
48214      * is called when the editor creates its toolbar. Override this method if you need to
48215      * add custom toolbar buttons.
48216      * @param {HtmlEditor} editor
48217      */
48218     createToolbar : function(editor){
48219         Roo.log("create toolbars");
48220         if (!editor.toolbars || !editor.toolbars.length) {
48221             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48222         }
48223         
48224         for (var i =0 ; i < editor.toolbars.length;i++) {
48225             editor.toolbars[i] = Roo.factory(
48226                     typeof(editor.toolbars[i]) == 'string' ?
48227                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48228                 Roo.form.HtmlEditor);
48229             editor.toolbars[i].init(editor);
48230         }
48231          
48232         
48233     },
48234
48235      
48236     // private
48237     onRender : function(ct, position)
48238     {
48239         var _t = this;
48240         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48241         
48242         this.wrap = this.el.wrap({
48243             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48244         });
48245         
48246         this.editorcore.onRender(ct, position);
48247          
48248         if (this.resizable) {
48249             this.resizeEl = new Roo.Resizable(this.wrap, {
48250                 pinned : true,
48251                 wrap: true,
48252                 dynamic : true,
48253                 minHeight : this.height,
48254                 height: this.height,
48255                 handles : this.resizable,
48256                 width: this.width,
48257                 listeners : {
48258                     resize : function(r, w, h) {
48259                         _t.onResize(w,h); // -something
48260                     }
48261                 }
48262             });
48263             
48264         }
48265         this.createToolbar(this);
48266        
48267         
48268         if(!this.width){
48269             this.setSize(this.wrap.getSize());
48270         }
48271         if (this.resizeEl) {
48272             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48273             // should trigger onReize..
48274         }
48275         
48276         this.keyNav = new Roo.KeyNav(this.el, {
48277             
48278             "tab" : function(e){
48279                 e.preventDefault();
48280                 
48281                 var value = this.getValue();
48282                 
48283                 var start = this.el.dom.selectionStart;
48284                 var end = this.el.dom.selectionEnd;
48285                 
48286                 if(!e.shiftKey){
48287                     
48288                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48289                     this.el.dom.setSelectionRange(end + 1, end + 1);
48290                     return;
48291                 }
48292                 
48293                 var f = value.substring(0, start).split("\t");
48294                 
48295                 if(f.pop().length != 0){
48296                     return;
48297                 }
48298                 
48299                 this.setValue(f.join("\t") + value.substring(end));
48300                 this.el.dom.setSelectionRange(start - 1, start - 1);
48301                 
48302             },
48303             
48304             "home" : function(e){
48305                 e.preventDefault();
48306                 
48307                 var curr = this.el.dom.selectionStart;
48308                 var lines = this.getValue().split("\n");
48309                 
48310                 if(!lines.length){
48311                     return;
48312                 }
48313                 
48314                 if(e.ctrlKey){
48315                     this.el.dom.setSelectionRange(0, 0);
48316                     return;
48317                 }
48318                 
48319                 var pos = 0;
48320                 
48321                 for (var i = 0; i < lines.length;i++) {
48322                     pos += lines[i].length;
48323                     
48324                     if(i != 0){
48325                         pos += 1;
48326                     }
48327                     
48328                     if(pos < curr){
48329                         continue;
48330                     }
48331                     
48332                     pos -= lines[i].length;
48333                     
48334                     break;
48335                 }
48336                 
48337                 if(!e.shiftKey){
48338                     this.el.dom.setSelectionRange(pos, pos);
48339                     return;
48340                 }
48341                 
48342                 this.el.dom.selectionStart = pos;
48343                 this.el.dom.selectionEnd = curr;
48344             },
48345             
48346             "end" : function(e){
48347                 e.preventDefault();
48348                 
48349                 var curr = this.el.dom.selectionStart;
48350                 var lines = this.getValue().split("\n");
48351                 
48352                 if(!lines.length){
48353                     return;
48354                 }
48355                 
48356                 if(e.ctrlKey){
48357                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48358                     return;
48359                 }
48360                 
48361                 var pos = 0;
48362                 
48363                 for (var i = 0; i < lines.length;i++) {
48364                     
48365                     pos += lines[i].length;
48366                     
48367                     if(i != 0){
48368                         pos += 1;
48369                     }
48370                     
48371                     if(pos < curr){
48372                         continue;
48373                     }
48374                     
48375                     break;
48376                 }
48377                 
48378                 if(!e.shiftKey){
48379                     this.el.dom.setSelectionRange(pos, pos);
48380                     return;
48381                 }
48382                 
48383                 this.el.dom.selectionStart = curr;
48384                 this.el.dom.selectionEnd = pos;
48385             },
48386
48387             scope : this,
48388
48389             doRelay : function(foo, bar, hname){
48390                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48391             },
48392
48393             forceKeyDown: true
48394         });
48395         
48396 //        if(this.autosave && this.w){
48397 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48398 //        }
48399     },
48400
48401     // private
48402     onResize : function(w, h)
48403     {
48404         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48405         var ew = false;
48406         var eh = false;
48407         
48408         if(this.el ){
48409             if(typeof w == 'number'){
48410                 var aw = w - this.wrap.getFrameWidth('lr');
48411                 this.el.setWidth(this.adjustWidth('textarea', aw));
48412                 ew = aw;
48413             }
48414             if(typeof h == 'number'){
48415                 var tbh = 0;
48416                 for (var i =0; i < this.toolbars.length;i++) {
48417                     // fixme - ask toolbars for heights?
48418                     tbh += this.toolbars[i].tb.el.getHeight();
48419                     if (this.toolbars[i].footer) {
48420                         tbh += this.toolbars[i].footer.el.getHeight();
48421                     }
48422                 }
48423                 
48424                 
48425                 
48426                 
48427                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48428                 ah -= 5; // knock a few pixes off for look..
48429 //                Roo.log(ah);
48430                 this.el.setHeight(this.adjustWidth('textarea', ah));
48431                 var eh = ah;
48432             }
48433         }
48434         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48435         this.editorcore.onResize(ew,eh);
48436         
48437     },
48438
48439     /**
48440      * Toggles the editor between standard and source edit mode.
48441      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48442      */
48443     toggleSourceEdit : function(sourceEditMode)
48444     {
48445         this.editorcore.toggleSourceEdit(sourceEditMode);
48446         
48447         if(this.editorcore.sourceEditMode){
48448             Roo.log('editor - showing textarea');
48449             
48450 //            Roo.log('in');
48451 //            Roo.log(this.syncValue());
48452             this.editorcore.syncValue();
48453             this.el.removeClass('x-hidden');
48454             this.el.dom.removeAttribute('tabIndex');
48455             this.el.focus();
48456             this.el.dom.scrollTop = 0;
48457             
48458             
48459             for (var i = 0; i < this.toolbars.length; i++) {
48460                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48461                     this.toolbars[i].tb.hide();
48462                     this.toolbars[i].footer.hide();
48463                 }
48464             }
48465             
48466         }else{
48467             Roo.log('editor - hiding textarea');
48468 //            Roo.log('out')
48469 //            Roo.log(this.pushValue()); 
48470             this.editorcore.pushValue();
48471             
48472             this.el.addClass('x-hidden');
48473             this.el.dom.setAttribute('tabIndex', -1);
48474             
48475             for (var i = 0; i < this.toolbars.length; i++) {
48476                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48477                     this.toolbars[i].tb.show();
48478                     this.toolbars[i].footer.show();
48479                 }
48480             }
48481             
48482             //this.deferFocus();
48483         }
48484         
48485         this.setSize(this.wrap.getSize());
48486         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48487         
48488         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48489     },
48490  
48491     // private (for BoxComponent)
48492     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48493
48494     // private (for BoxComponent)
48495     getResizeEl : function(){
48496         return this.wrap;
48497     },
48498
48499     // private (for BoxComponent)
48500     getPositionEl : function(){
48501         return this.wrap;
48502     },
48503
48504     // private
48505     initEvents : function(){
48506         this.originalValue = this.getValue();
48507     },
48508
48509     /**
48510      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48511      * @method
48512      */
48513     markInvalid : Roo.emptyFn,
48514     /**
48515      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48516      * @method
48517      */
48518     clearInvalid : Roo.emptyFn,
48519
48520     setValue : function(v){
48521         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48522         this.editorcore.pushValue();
48523     },
48524
48525      
48526     // private
48527     deferFocus : function(){
48528         this.focus.defer(10, this);
48529     },
48530
48531     // doc'ed in Field
48532     focus : function(){
48533         this.editorcore.focus();
48534         
48535     },
48536       
48537
48538     // private
48539     onDestroy : function(){
48540         
48541         
48542         
48543         if(this.rendered){
48544             
48545             for (var i =0; i < this.toolbars.length;i++) {
48546                 // fixme - ask toolbars for heights?
48547                 this.toolbars[i].onDestroy();
48548             }
48549             
48550             this.wrap.dom.innerHTML = '';
48551             this.wrap.remove();
48552         }
48553     },
48554
48555     // private
48556     onFirstFocus : function(){
48557         //Roo.log("onFirstFocus");
48558         this.editorcore.onFirstFocus();
48559          for (var i =0; i < this.toolbars.length;i++) {
48560             this.toolbars[i].onFirstFocus();
48561         }
48562         
48563     },
48564     
48565     // private
48566     syncValue : function()
48567     {
48568         this.editorcore.syncValue();
48569     },
48570     
48571     pushValue : function()
48572     {
48573         this.editorcore.pushValue();
48574     },
48575     
48576     setStylesheets : function(stylesheets)
48577     {
48578         this.editorcore.setStylesheets(stylesheets);
48579     },
48580     
48581     removeStylesheets : function()
48582     {
48583         this.editorcore.removeStylesheets();
48584     }
48585      
48586     
48587     // hide stuff that is not compatible
48588     /**
48589      * @event blur
48590      * @hide
48591      */
48592     /**
48593      * @event change
48594      * @hide
48595      */
48596     /**
48597      * @event focus
48598      * @hide
48599      */
48600     /**
48601      * @event specialkey
48602      * @hide
48603      */
48604     /**
48605      * @cfg {String} fieldClass @hide
48606      */
48607     /**
48608      * @cfg {String} focusClass @hide
48609      */
48610     /**
48611      * @cfg {String} autoCreate @hide
48612      */
48613     /**
48614      * @cfg {String} inputType @hide
48615      */
48616     /**
48617      * @cfg {String} invalidClass @hide
48618      */
48619     /**
48620      * @cfg {String} invalidText @hide
48621      */
48622     /**
48623      * @cfg {String} msgFx @hide
48624      */
48625     /**
48626      * @cfg {String} validateOnBlur @hide
48627      */
48628 });
48629  
48630     // <script type="text/javascript">
48631 /*
48632  * Based on
48633  * Ext JS Library 1.1.1
48634  * Copyright(c) 2006-2007, Ext JS, LLC.
48635  *  
48636  
48637  */
48638
48639 /**
48640  * @class Roo.form.HtmlEditorToolbar1
48641  * Basic Toolbar
48642  * 
48643  * Usage:
48644  *
48645  new Roo.form.HtmlEditor({
48646     ....
48647     toolbars : [
48648         new Roo.form.HtmlEditorToolbar1({
48649             disable : { fonts: 1 , format: 1, ..., ... , ...],
48650             btns : [ .... ]
48651         })
48652     }
48653      
48654  * 
48655  * @cfg {Object} disable List of elements to disable..
48656  * @cfg {Array} btns List of additional buttons.
48657  * 
48658  * 
48659  * NEEDS Extra CSS? 
48660  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48661  */
48662  
48663 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48664 {
48665     
48666     Roo.apply(this, config);
48667     
48668     // default disabled, based on 'good practice'..
48669     this.disable = this.disable || {};
48670     Roo.applyIf(this.disable, {
48671         fontSize : true,
48672         colors : true,
48673         specialElements : true
48674     });
48675     
48676     
48677     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48678     // dont call parent... till later.
48679 }
48680
48681 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48682     
48683     tb: false,
48684     
48685     rendered: false,
48686     
48687     editor : false,
48688     editorcore : false,
48689     /**
48690      * @cfg {Object} disable  List of toolbar elements to disable
48691          
48692      */
48693     disable : false,
48694     
48695     
48696      /**
48697      * @cfg {String} createLinkText The default text for the create link prompt
48698      */
48699     createLinkText : 'Please enter the URL for the link:',
48700     /**
48701      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48702      */
48703     defaultLinkValue : 'http:/'+'/',
48704    
48705     
48706       /**
48707      * @cfg {Array} fontFamilies An array of available font families
48708      */
48709     fontFamilies : [
48710         'Arial',
48711         'Courier New',
48712         'Tahoma',
48713         'Times New Roman',
48714         'Verdana'
48715     ],
48716     
48717     specialChars : [
48718            "&#169;",
48719           "&#174;",     
48720           "&#8482;",    
48721           "&#163;" ,    
48722          // "&#8212;",    
48723           "&#8230;",    
48724           "&#247;" ,    
48725         //  "&#225;" ,     ?? a acute?
48726            "&#8364;"    , //Euro
48727        //   "&#8220;"    ,
48728         //  "&#8221;"    ,
48729         //  "&#8226;"    ,
48730           "&#176;"  //   , // degrees
48731
48732          // "&#233;"     , // e ecute
48733          // "&#250;"     , // u ecute?
48734     ],
48735     
48736     specialElements : [
48737         {
48738             text: "Insert Table",
48739             xtype: 'MenuItem',
48740             xns : Roo.Menu,
48741             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48742                 
48743         },
48744         {    
48745             text: "Insert Image",
48746             xtype: 'MenuItem',
48747             xns : Roo.Menu,
48748             ihtml : '<img src="about:blank"/>'
48749             
48750         }
48751         
48752          
48753     ],
48754     
48755     
48756     inputElements : [ 
48757             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48758             "input:submit", "input:button", "select", "textarea", "label" ],
48759     formats : [
48760         ["p"] ,  
48761         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48762         ["pre"],[ "code"], 
48763         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48764         ['div'],['span'],
48765         ['sup'],['sub']
48766     ],
48767     
48768     cleanStyles : [
48769         "font-size"
48770     ],
48771      /**
48772      * @cfg {String} defaultFont default font to use.
48773      */
48774     defaultFont: 'tahoma',
48775    
48776     fontSelect : false,
48777     
48778     
48779     formatCombo : false,
48780     
48781     init : function(editor)
48782     {
48783         this.editor = editor;
48784         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48785         var editorcore = this.editorcore;
48786         
48787         var _t = this;
48788         
48789         var fid = editorcore.frameId;
48790         var etb = this;
48791         function btn(id, toggle, handler){
48792             var xid = fid + '-'+ id ;
48793             return {
48794                 id : xid,
48795                 cmd : id,
48796                 cls : 'x-btn-icon x-edit-'+id,
48797                 enableToggle:toggle !== false,
48798                 scope: _t, // was editor...
48799                 handler:handler||_t.relayBtnCmd,
48800                 clickEvent:'mousedown',
48801                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48802                 tabIndex:-1
48803             };
48804         }
48805         
48806         
48807         
48808         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48809         this.tb = tb;
48810          // stop form submits
48811         tb.el.on('click', function(e){
48812             e.preventDefault(); // what does this do?
48813         });
48814
48815         if(!this.disable.font) { // && !Roo.isSafari){
48816             /* why no safari for fonts 
48817             editor.fontSelect = tb.el.createChild({
48818                 tag:'select',
48819                 tabIndex: -1,
48820                 cls:'x-font-select',
48821                 html: this.createFontOptions()
48822             });
48823             
48824             editor.fontSelect.on('change', function(){
48825                 var font = editor.fontSelect.dom.value;
48826                 editor.relayCmd('fontname', font);
48827                 editor.deferFocus();
48828             }, editor);
48829             
48830             tb.add(
48831                 editor.fontSelect.dom,
48832                 '-'
48833             );
48834             */
48835             
48836         };
48837         if(!this.disable.formats){
48838             this.formatCombo = new Roo.form.ComboBox({
48839                 store: new Roo.data.SimpleStore({
48840                     id : 'tag',
48841                     fields: ['tag'],
48842                     data : this.formats // from states.js
48843                 }),
48844                 blockFocus : true,
48845                 name : '',
48846                 //autoCreate : {tag: "div",  size: "20"},
48847                 displayField:'tag',
48848                 typeAhead: false,
48849                 mode: 'local',
48850                 editable : false,
48851                 triggerAction: 'all',
48852                 emptyText:'Add tag',
48853                 selectOnFocus:true,
48854                 width:135,
48855                 listeners : {
48856                     'select': function(c, r, i) {
48857                         editorcore.insertTag(r.get('tag'));
48858                         editor.focus();
48859                     }
48860                 }
48861
48862             });
48863             tb.addField(this.formatCombo);
48864             
48865         }
48866         
48867         if(!this.disable.format){
48868             tb.add(
48869                 btn('bold'),
48870                 btn('italic'),
48871                 btn('underline'),
48872                 btn('strikethrough')
48873             );
48874         };
48875         if(!this.disable.fontSize){
48876             tb.add(
48877                 '-',
48878                 
48879                 
48880                 btn('increasefontsize', false, editorcore.adjustFont),
48881                 btn('decreasefontsize', false, editorcore.adjustFont)
48882             );
48883         };
48884         
48885         
48886         if(!this.disable.colors){
48887             tb.add(
48888                 '-', {
48889                     id:editorcore.frameId +'-forecolor',
48890                     cls:'x-btn-icon x-edit-forecolor',
48891                     clickEvent:'mousedown',
48892                     tooltip: this.buttonTips['forecolor'] || undefined,
48893                     tabIndex:-1,
48894                     menu : new Roo.menu.ColorMenu({
48895                         allowReselect: true,
48896                         focus: Roo.emptyFn,
48897                         value:'000000',
48898                         plain:true,
48899                         selectHandler: function(cp, color){
48900                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48901                             editor.deferFocus();
48902                         },
48903                         scope: editorcore,
48904                         clickEvent:'mousedown'
48905                     })
48906                 }, {
48907                     id:editorcore.frameId +'backcolor',
48908                     cls:'x-btn-icon x-edit-backcolor',
48909                     clickEvent:'mousedown',
48910                     tooltip: this.buttonTips['backcolor'] || undefined,
48911                     tabIndex:-1,
48912                     menu : new Roo.menu.ColorMenu({
48913                         focus: Roo.emptyFn,
48914                         value:'FFFFFF',
48915                         plain:true,
48916                         allowReselect: true,
48917                         selectHandler: function(cp, color){
48918                             if(Roo.isGecko){
48919                                 editorcore.execCmd('useCSS', false);
48920                                 editorcore.execCmd('hilitecolor', color);
48921                                 editorcore.execCmd('useCSS', true);
48922                                 editor.deferFocus();
48923                             }else{
48924                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48925                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48926                                 editor.deferFocus();
48927                             }
48928                         },
48929                         scope:editorcore,
48930                         clickEvent:'mousedown'
48931                     })
48932                 }
48933             );
48934         };
48935         // now add all the items...
48936         
48937
48938         if(!this.disable.alignments){
48939             tb.add(
48940                 '-',
48941                 btn('justifyleft'),
48942                 btn('justifycenter'),
48943                 btn('justifyright')
48944             );
48945         };
48946
48947         //if(!Roo.isSafari){
48948             if(!this.disable.links){
48949                 tb.add(
48950                     '-',
48951                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48952                 );
48953             };
48954
48955             if(!this.disable.lists){
48956                 tb.add(
48957                     '-',
48958                     btn('insertorderedlist'),
48959                     btn('insertunorderedlist')
48960                 );
48961             }
48962             if(!this.disable.sourceEdit){
48963                 tb.add(
48964                     '-',
48965                     btn('sourceedit', true, function(btn){
48966                         this.toggleSourceEdit(btn.pressed);
48967                     })
48968                 );
48969             }
48970         //}
48971         
48972         var smenu = { };
48973         // special menu.. - needs to be tidied up..
48974         if (!this.disable.special) {
48975             smenu = {
48976                 text: "&#169;",
48977                 cls: 'x-edit-none',
48978                 
48979                 menu : {
48980                     items : []
48981                 }
48982             };
48983             for (var i =0; i < this.specialChars.length; i++) {
48984                 smenu.menu.items.push({
48985                     
48986                     html: this.specialChars[i],
48987                     handler: function(a,b) {
48988                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48989                         //editor.insertAtCursor(a.html);
48990                         
48991                     },
48992                     tabIndex:-1
48993                 });
48994             }
48995             
48996             
48997             tb.add(smenu);
48998             
48999             
49000         }
49001         
49002         var cmenu = { };
49003         if (!this.disable.cleanStyles) {
49004             cmenu = {
49005                 cls: 'x-btn-icon x-btn-clear',
49006                 
49007                 menu : {
49008                     items : []
49009                 }
49010             };
49011             for (var i =0; i < this.cleanStyles.length; i++) {
49012                 cmenu.menu.items.push({
49013                     actiontype : this.cleanStyles[i],
49014                     html: 'Remove ' + this.cleanStyles[i],
49015                     handler: function(a,b) {
49016 //                        Roo.log(a);
49017 //                        Roo.log(b);
49018                         var c = Roo.get(editorcore.doc.body);
49019                         c.select('[style]').each(function(s) {
49020                             s.dom.style.removeProperty(a.actiontype);
49021                         });
49022                         editorcore.syncValue();
49023                     },
49024                     tabIndex:-1
49025                 });
49026             }
49027             cmenu.menu.items.push({
49028                 actiontype : 'tablewidths',
49029                 html: 'Remove Table Widths',
49030                 handler: function(a,b) {
49031                     editorcore.cleanTableWidths();
49032                     editorcore.syncValue();
49033                 },
49034                 tabIndex:-1
49035             });
49036             cmenu.menu.items.push({
49037                 actiontype : 'word',
49038                 html: 'Remove MS Word Formating',
49039                 handler: function(a,b) {
49040                     editorcore.cleanWord();
49041                     editorcore.syncValue();
49042                 },
49043                 tabIndex:-1
49044             });
49045             
49046             cmenu.menu.items.push({
49047                 actiontype : 'all',
49048                 html: 'Remove All Styles',
49049                 handler: function(a,b) {
49050                     
49051                     var c = Roo.get(editorcore.doc.body);
49052                     c.select('[style]').each(function(s) {
49053                         s.dom.removeAttribute('style');
49054                     });
49055                     editorcore.syncValue();
49056                 },
49057                 tabIndex:-1
49058             });
49059             
49060             cmenu.menu.items.push({
49061                 actiontype : 'all',
49062                 html: 'Remove All CSS Classes',
49063                 handler: function(a,b) {
49064                     
49065                     var c = Roo.get(editorcore.doc.body);
49066                     c.select('[class]').each(function(s) {
49067                         s.dom.removeAttribute('class');
49068                     });
49069                     editorcore.cleanWord();
49070                     editorcore.syncValue();
49071                 },
49072                 tabIndex:-1
49073             });
49074             
49075              cmenu.menu.items.push({
49076                 actiontype : 'tidy',
49077                 html: 'Tidy HTML Source',
49078                 handler: function(a,b) {
49079                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49080                     editorcore.syncValue();
49081                 },
49082                 tabIndex:-1
49083             });
49084             
49085             
49086             tb.add(cmenu);
49087         }
49088          
49089         if (!this.disable.specialElements) {
49090             var semenu = {
49091                 text: "Other;",
49092                 cls: 'x-edit-none',
49093                 menu : {
49094                     items : []
49095                 }
49096             };
49097             for (var i =0; i < this.specialElements.length; i++) {
49098                 semenu.menu.items.push(
49099                     Roo.apply({ 
49100                         handler: function(a,b) {
49101                             editor.insertAtCursor(this.ihtml);
49102                         }
49103                     }, this.specialElements[i])
49104                 );
49105                     
49106             }
49107             
49108             tb.add(semenu);
49109             
49110             
49111         }
49112          
49113         
49114         if (this.btns) {
49115             for(var i =0; i< this.btns.length;i++) {
49116                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49117                 b.cls =  'x-edit-none';
49118                 
49119                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49120                     b.cls += ' x-init-enable';
49121                 }
49122                 
49123                 b.scope = editorcore;
49124                 tb.add(b);
49125             }
49126         
49127         }
49128         
49129         
49130         
49131         // disable everything...
49132         
49133         this.tb.items.each(function(item){
49134             
49135            if(
49136                 item.id != editorcore.frameId+ '-sourceedit' && 
49137                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49138             ){
49139                 
49140                 item.disable();
49141             }
49142         });
49143         this.rendered = true;
49144         
49145         // the all the btns;
49146         editor.on('editorevent', this.updateToolbar, this);
49147         // other toolbars need to implement this..
49148         //editor.on('editmodechange', this.updateToolbar, this);
49149     },
49150     
49151     
49152     relayBtnCmd : function(btn) {
49153         this.editorcore.relayCmd(btn.cmd);
49154     },
49155     // private used internally
49156     createLink : function(){
49157         Roo.log("create link?");
49158         var url = prompt(this.createLinkText, this.defaultLinkValue);
49159         if(url && url != 'http:/'+'/'){
49160             this.editorcore.relayCmd('createlink', url);
49161         }
49162     },
49163
49164     
49165     /**
49166      * Protected method that will not generally be called directly. It triggers
49167      * a toolbar update by reading the markup state of the current selection in the editor.
49168      */
49169     updateToolbar: function(){
49170
49171         if(!this.editorcore.activated){
49172             this.editor.onFirstFocus();
49173             return;
49174         }
49175
49176         var btns = this.tb.items.map, 
49177             doc = this.editorcore.doc,
49178             frameId = this.editorcore.frameId;
49179
49180         if(!this.disable.font && !Roo.isSafari){
49181             /*
49182             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49183             if(name != this.fontSelect.dom.value){
49184                 this.fontSelect.dom.value = name;
49185             }
49186             */
49187         }
49188         if(!this.disable.format){
49189             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49190             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49191             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49192             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49193         }
49194         if(!this.disable.alignments){
49195             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49196             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49197             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49198         }
49199         if(!Roo.isSafari && !this.disable.lists){
49200             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49201             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49202         }
49203         
49204         var ans = this.editorcore.getAllAncestors();
49205         if (this.formatCombo) {
49206             
49207             
49208             var store = this.formatCombo.store;
49209             this.formatCombo.setValue("");
49210             for (var i =0; i < ans.length;i++) {
49211                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49212                     // select it..
49213                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49214                     break;
49215                 }
49216             }
49217         }
49218         
49219         
49220         
49221         // hides menus... - so this cant be on a menu...
49222         Roo.menu.MenuMgr.hideAll();
49223
49224         //this.editorsyncValue();
49225     },
49226    
49227     
49228     createFontOptions : function(){
49229         var buf = [], fs = this.fontFamilies, ff, lc;
49230         
49231         
49232         
49233         for(var i = 0, len = fs.length; i< len; i++){
49234             ff = fs[i];
49235             lc = ff.toLowerCase();
49236             buf.push(
49237                 '<option value="',lc,'" style="font-family:',ff,';"',
49238                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49239                     ff,
49240                 '</option>'
49241             );
49242         }
49243         return buf.join('');
49244     },
49245     
49246     toggleSourceEdit : function(sourceEditMode){
49247         
49248         Roo.log("toolbar toogle");
49249         if(sourceEditMode === undefined){
49250             sourceEditMode = !this.sourceEditMode;
49251         }
49252         this.sourceEditMode = sourceEditMode === true;
49253         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49254         // just toggle the button?
49255         if(btn.pressed !== this.sourceEditMode){
49256             btn.toggle(this.sourceEditMode);
49257             return;
49258         }
49259         
49260         if(sourceEditMode){
49261             Roo.log("disabling buttons");
49262             this.tb.items.each(function(item){
49263                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49264                     item.disable();
49265                 }
49266             });
49267           
49268         }else{
49269             Roo.log("enabling buttons");
49270             if(this.editorcore.initialized){
49271                 this.tb.items.each(function(item){
49272                     item.enable();
49273                 });
49274             }
49275             
49276         }
49277         Roo.log("calling toggole on editor");
49278         // tell the editor that it's been pressed..
49279         this.editor.toggleSourceEdit(sourceEditMode);
49280        
49281     },
49282      /**
49283      * Object collection of toolbar tooltips for the buttons in the editor. The key
49284      * is the command id associated with that button and the value is a valid QuickTips object.
49285      * For example:
49286 <pre><code>
49287 {
49288     bold : {
49289         title: 'Bold (Ctrl+B)',
49290         text: 'Make the selected text bold.',
49291         cls: 'x-html-editor-tip'
49292     },
49293     italic : {
49294         title: 'Italic (Ctrl+I)',
49295         text: 'Make the selected text italic.',
49296         cls: 'x-html-editor-tip'
49297     },
49298     ...
49299 </code></pre>
49300     * @type Object
49301      */
49302     buttonTips : {
49303         bold : {
49304             title: 'Bold (Ctrl+B)',
49305             text: 'Make the selected text bold.',
49306             cls: 'x-html-editor-tip'
49307         },
49308         italic : {
49309             title: 'Italic (Ctrl+I)',
49310             text: 'Make the selected text italic.',
49311             cls: 'x-html-editor-tip'
49312         },
49313         underline : {
49314             title: 'Underline (Ctrl+U)',
49315             text: 'Underline the selected text.',
49316             cls: 'x-html-editor-tip'
49317         },
49318         strikethrough : {
49319             title: 'Strikethrough',
49320             text: 'Strikethrough the selected text.',
49321             cls: 'x-html-editor-tip'
49322         },
49323         increasefontsize : {
49324             title: 'Grow Text',
49325             text: 'Increase the font size.',
49326             cls: 'x-html-editor-tip'
49327         },
49328         decreasefontsize : {
49329             title: 'Shrink Text',
49330             text: 'Decrease the font size.',
49331             cls: 'x-html-editor-tip'
49332         },
49333         backcolor : {
49334             title: 'Text Highlight Color',
49335             text: 'Change the background color of the selected text.',
49336             cls: 'x-html-editor-tip'
49337         },
49338         forecolor : {
49339             title: 'Font Color',
49340             text: 'Change the color of the selected text.',
49341             cls: 'x-html-editor-tip'
49342         },
49343         justifyleft : {
49344             title: 'Align Text Left',
49345             text: 'Align text to the left.',
49346             cls: 'x-html-editor-tip'
49347         },
49348         justifycenter : {
49349             title: 'Center Text',
49350             text: 'Center text in the editor.',
49351             cls: 'x-html-editor-tip'
49352         },
49353         justifyright : {
49354             title: 'Align Text Right',
49355             text: 'Align text to the right.',
49356             cls: 'x-html-editor-tip'
49357         },
49358         insertunorderedlist : {
49359             title: 'Bullet List',
49360             text: 'Start a bulleted list.',
49361             cls: 'x-html-editor-tip'
49362         },
49363         insertorderedlist : {
49364             title: 'Numbered List',
49365             text: 'Start a numbered list.',
49366             cls: 'x-html-editor-tip'
49367         },
49368         createlink : {
49369             title: 'Hyperlink',
49370             text: 'Make the selected text a hyperlink.',
49371             cls: 'x-html-editor-tip'
49372         },
49373         sourceedit : {
49374             title: 'Source Edit',
49375             text: 'Switch to source editing mode.',
49376             cls: 'x-html-editor-tip'
49377         }
49378     },
49379     // private
49380     onDestroy : function(){
49381         if(this.rendered){
49382             
49383             this.tb.items.each(function(item){
49384                 if(item.menu){
49385                     item.menu.removeAll();
49386                     if(item.menu.el){
49387                         item.menu.el.destroy();
49388                     }
49389                 }
49390                 item.destroy();
49391             });
49392              
49393         }
49394     },
49395     onFirstFocus: function() {
49396         this.tb.items.each(function(item){
49397            item.enable();
49398         });
49399     }
49400 });
49401
49402
49403
49404
49405 // <script type="text/javascript">
49406 /*
49407  * Based on
49408  * Ext JS Library 1.1.1
49409  * Copyright(c) 2006-2007, Ext JS, LLC.
49410  *  
49411  
49412  */
49413
49414  
49415 /**
49416  * @class Roo.form.HtmlEditor.ToolbarContext
49417  * Context Toolbar
49418  * 
49419  * Usage:
49420  *
49421  new Roo.form.HtmlEditor({
49422     ....
49423     toolbars : [
49424         { xtype: 'ToolbarStandard', styles : {} }
49425         { xtype: 'ToolbarContext', disable : {} }
49426     ]
49427 })
49428
49429      
49430  * 
49431  * @config : {Object} disable List of elements to disable.. (not done yet.)
49432  * @config : {Object} styles  Map of styles available.
49433  * 
49434  */
49435
49436 Roo.form.HtmlEditor.ToolbarContext = function(config)
49437 {
49438     
49439     Roo.apply(this, config);
49440     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49441     // dont call parent... till later.
49442     this.styles = this.styles || {};
49443 }
49444
49445  
49446
49447 Roo.form.HtmlEditor.ToolbarContext.types = {
49448     'IMG' : [
49449         {
49450             name : 'width',
49451             title: "Width",
49452             width: 40
49453         },
49454         {
49455             name : 'height',
49456             title: "Height",
49457             width: 40
49458         },
49459         {
49460             name : 'align',
49461             title: "Align",
49462             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49463             width : 80
49464             
49465         },
49466         {
49467             name : 'border',
49468             title: "Border",
49469             width: 40
49470         },
49471         {
49472             name : 'alt',
49473             title: "Alt",
49474             width: 120
49475         },
49476         {
49477             name : 'src',
49478             title: "Src",
49479             width: 220
49480         }
49481         
49482     ],
49483     
49484     'FIGURE' : [
49485         {
49486             name : 'align',
49487             title: "Align",
49488             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49489             width : 80  
49490         }
49491     ],
49492     'A' : [
49493         {
49494             name : 'name',
49495             title: "Name",
49496             width: 50
49497         },
49498         {
49499             name : 'target',
49500             title: "Target",
49501             width: 120
49502         },
49503         {
49504             name : 'href',
49505             title: "Href",
49506             width: 220
49507         } // border?
49508         
49509     ],
49510     
49511     'INPUT' : [
49512         {
49513             name : 'name',
49514             title: "name",
49515             width: 120
49516         },
49517         {
49518             name : 'value',
49519             title: "Value",
49520             width: 120
49521         },
49522         {
49523             name : 'width',
49524             title: "Width",
49525             width: 40
49526         }
49527     ],
49528     'LABEL' : [
49529          {
49530             name : 'for',
49531             title: "For",
49532             width: 120
49533         }
49534     ],
49535     'TEXTAREA' : [
49536         {
49537             name : 'name',
49538             title: "name",
49539             width: 120
49540         },
49541         {
49542             name : 'rows',
49543             title: "Rows",
49544             width: 20
49545         },
49546         {
49547             name : 'cols',
49548             title: "Cols",
49549             width: 20
49550         }
49551     ],
49552     'SELECT' : [
49553         {
49554             name : 'name',
49555             title: "name",
49556             width: 120
49557         },
49558         {
49559             name : 'selectoptions',
49560             title: "Options",
49561             width: 200
49562         }
49563     ],
49564     
49565     // should we really allow this??
49566     // should this just be 
49567     'BODY' : [
49568         
49569         {
49570             name : 'title',
49571             title: "Title",
49572             width: 200,
49573             disabled : true
49574         }
49575     ],
49576  
49577     '*' : [
49578         // empty.
49579     ]
49580
49581 };
49582
49583 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49584 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49585
49586 Roo.form.HtmlEditor.ToolbarContext.options = {
49587         'font-family'  : [ 
49588                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49589                 [ 'Courier New', 'Courier New'],
49590                 [ 'Tahoma', 'Tahoma'],
49591                 [ 'Times New Roman,serif', 'Times'],
49592                 [ 'Verdana','Verdana' ]
49593         ]
49594 };
49595
49596 // fixme - these need to be configurable..
49597  
49598
49599 //Roo.form.HtmlEditor.ToolbarContext.types
49600
49601
49602 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49603     
49604     tb: false,
49605     
49606     rendered: false,
49607     
49608     editor : false,
49609     editorcore : false,
49610     /**
49611      * @cfg {Object} disable  List of toolbar elements to disable
49612          
49613      */
49614     disable : false,
49615     /**
49616      * @cfg {Object} styles List of styles 
49617      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49618      *
49619      * These must be defined in the page, so they get rendered correctly..
49620      * .headline { }
49621      * TD.underline { }
49622      * 
49623      */
49624     styles : false,
49625     
49626     options: false,
49627     
49628     toolbars : false,
49629     
49630     init : function(editor)
49631     {
49632         this.editor = editor;
49633         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49634         var editorcore = this.editorcore;
49635         
49636         var fid = editorcore.frameId;
49637         var etb = this;
49638         function btn(id, toggle, handler){
49639             var xid = fid + '-'+ id ;
49640             return {
49641                 id : xid,
49642                 cmd : id,
49643                 cls : 'x-btn-icon x-edit-'+id,
49644                 enableToggle:toggle !== false,
49645                 scope: editorcore, // was editor...
49646                 handler:handler||editorcore.relayBtnCmd,
49647                 clickEvent:'mousedown',
49648                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49649                 tabIndex:-1
49650             };
49651         }
49652         // create a new element.
49653         var wdiv = editor.wrap.createChild({
49654                 tag: 'div'
49655             }, editor.wrap.dom.firstChild.nextSibling, true);
49656         
49657         // can we do this more than once??
49658         
49659          // stop form submits
49660       
49661  
49662         // disable everything...
49663         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49664         this.toolbars = {};
49665            
49666         for (var i in  ty) {
49667           
49668             this.toolbars[i] = this.buildToolbar(ty[i],i);
49669         }
49670         this.tb = this.toolbars.BODY;
49671         this.tb.el.show();
49672         this.buildFooter();
49673         this.footer.show();
49674         editor.on('hide', function( ) { this.footer.hide() }, this);
49675         editor.on('show', function( ) { this.footer.show() }, this);
49676         
49677          
49678         this.rendered = true;
49679         
49680         // the all the btns;
49681         editor.on('editorevent', this.updateToolbar, this);
49682         // other toolbars need to implement this..
49683         //editor.on('editmodechange', this.updateToolbar, this);
49684     },
49685     
49686     
49687     
49688     /**
49689      * Protected method that will not generally be called directly. It triggers
49690      * a toolbar update by reading the markup state of the current selection in the editor.
49691      *
49692      * Note you can force an update by calling on('editorevent', scope, false)
49693      */
49694     updateToolbar: function(editor ,ev, sel)
49695     {
49696         
49697         if (ev) {
49698             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49699         }
49700         
49701         //Roo.log(ev);
49702         // capture mouse up - this is handy for selecting images..
49703         // perhaps should go somewhere else...
49704         if(!this.editorcore.activated){
49705              this.editor.onFirstFocus();
49706             return;
49707         }
49708         //Roo.log(ev ? ev.target : 'NOTARGET');
49709         
49710         
49711         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49712         // selectNode - might want to handle IE?
49713         
49714         
49715         
49716         if (ev &&
49717             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49718             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49719             // they have click on an image...
49720             // let's see if we can change the selection...
49721             sel = ev.target;
49722             
49723             // this triggers looping?
49724             //this.editorcore.selectNode(sel);
49725              
49726         }  
49727         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49728         //Roo.get(node).addClass('roo-ed-selection');
49729       
49730         //var updateFooter = sel ? false : true; 
49731         
49732         
49733         var ans = this.editorcore.getAllAncestors();
49734         
49735         // pick
49736         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49737         
49738         if (!sel) { 
49739             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49740             sel = sel ? sel : this.editorcore.doc.body;
49741             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49742             
49743         }
49744         
49745         var tn = sel.tagName.toUpperCase();
49746         var lastSel = this.tb.selectedNode;
49747         this.tb.selectedNode = sel;
49748         var left_label = tn;
49749         
49750         // ok see if we are editing a block?
49751         
49752         var db = false;
49753         // you are not actually selecting the block.
49754         if (sel && sel.hasAttribute('data-block')) {
49755             db = sel;
49756         } else if (sel && !sel.hasAttribute('contenteditable')) {
49757             var sel_el = Roo.get(sel);
49758             db = sel_el.findParent('[data-block]');
49759             var cepar = sel_el.findParent('[contenteditable=true]');
49760             if (db && cepar && cepar.tagName != 'BODY') {
49761                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49762             }   
49763         }
49764         
49765         
49766         var block = false;
49767         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49768         if (db) {
49769             block = Roo.htmleditor.Block.factory(db);
49770             
49771             
49772             if (block) {
49773                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
49774                 tn = 'BLOCK.' + db.getAttribute('data-block');
49775                 
49776                 //this.editorcore.selectNode(db);
49777                 if (typeof(this.toolbars[tn]) == 'undefined') {
49778                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49779                 }
49780                 this.toolbars[tn].selectedNode = db;
49781                 left_label = block.friendly_name;
49782                 ans = this.editorcore.getAllAncestors();
49783             }
49784             
49785                 
49786             
49787         }
49788         
49789         
49790         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49791             return; // no change?
49792         }
49793         
49794         
49795           
49796         this.tb.el.hide();
49797         ///console.log("show: " + tn);
49798         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49799         
49800         this.tb.el.show();
49801         // update name
49802         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49803         
49804         
49805         // update attributes
49806         if (block && this.tb.fields) {
49807              
49808             this.tb.fields.each(function(e) {
49809                 e.setValue(block[e.name]);
49810             });
49811             
49812             
49813         } else  if (this.tb.fields && this.tb.selectedNode) {
49814             this.tb.fields.each( function(e) {
49815                 if (e.stylename) {
49816                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49817                     return;
49818                 } 
49819                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49820             }, this);
49821             this.updateToolbarStyles(this.tb.selectedNode);  
49822         }
49823         
49824         
49825        
49826         Roo.menu.MenuMgr.hideAll();
49827
49828         
49829         
49830     
49831         // update the footer
49832         //
49833         this.updateFooter(ans);
49834              
49835     },
49836     
49837     updateToolbarStyles : function(sel)
49838     {
49839         var hasStyles = false;
49840         for(var i in this.styles) {
49841             hasStyles = true;
49842             break;
49843         }
49844         
49845         // update styles
49846         if (hasStyles && this.tb.hasStyles) { 
49847             var st = this.tb.fields.item(0);
49848             
49849             st.store.removeAll();
49850             var cn = sel.className.split(/\s+/);
49851             
49852             var avs = [];
49853             if (this.styles['*']) {
49854                 
49855                 Roo.each(this.styles['*'], function(v) {
49856                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49857                 });
49858             }
49859             if (this.styles[tn]) { 
49860                 Roo.each(this.styles[tn], function(v) {
49861                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49862                 });
49863             }
49864             
49865             st.store.loadData(avs);
49866             st.collapse();
49867             st.setValue(cn);
49868         }
49869     },
49870     
49871      
49872     updateFooter : function(ans)
49873     {
49874         var html = '';
49875         if (ans === false) {
49876             this.footDisp.dom.innerHTML = '';
49877             return;
49878         }
49879         
49880         this.footerEls = ans.reverse();
49881         Roo.each(this.footerEls, function(a,i) {
49882             if (!a) { return; }
49883             html += html.length ? ' &gt; '  :  '';
49884             
49885             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49886             
49887         });
49888        
49889         // 
49890         var sz = this.footDisp.up('td').getSize();
49891         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49892         this.footDisp.dom.style.marginLeft = '5px';
49893         
49894         this.footDisp.dom.style.overflow = 'hidden';
49895         
49896         this.footDisp.dom.innerHTML = html;
49897             
49898         
49899     },
49900    
49901        
49902     // private
49903     onDestroy : function(){
49904         if(this.rendered){
49905             
49906             this.tb.items.each(function(item){
49907                 if(item.menu){
49908                     item.menu.removeAll();
49909                     if(item.menu.el){
49910                         item.menu.el.destroy();
49911                     }
49912                 }
49913                 item.destroy();
49914             });
49915              
49916         }
49917     },
49918     onFirstFocus: function() {
49919         // need to do this for all the toolbars..
49920         this.tb.items.each(function(item){
49921            item.enable();
49922         });
49923     },
49924     buildToolbar: function(tlist, nm, friendly_name, block)
49925     {
49926         var editor = this.editor;
49927         var editorcore = this.editorcore;
49928          // create a new element.
49929         var wdiv = editor.wrap.createChild({
49930                 tag: 'div'
49931             }, editor.wrap.dom.firstChild.nextSibling, true);
49932         
49933        
49934         var tb = new Roo.Toolbar(wdiv);
49935         ///this.tb = tb; // << this sets the active toolbar..
49936         if (tlist === false && block) {
49937             tlist = block.contextMenu(this);
49938         }
49939         
49940         tb.hasStyles = false;
49941         tb.name = nm;
49942         
49943         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49944         
49945         var styles = Array.from(this.styles);
49946         
49947         
49948         // styles...
49949         if (styles && styles.length) {
49950             tb.hasStyles = true;
49951             // this needs a multi-select checkbox...
49952             tb.addField( new Roo.form.ComboBox({
49953                 store: new Roo.data.SimpleStore({
49954                     id : 'val',
49955                     fields: ['val', 'selected'],
49956                     data : [] 
49957                 }),
49958                 name : '-roo-edit-className',
49959                 attrname : 'className',
49960                 displayField: 'val',
49961                 typeAhead: false,
49962                 mode: 'local',
49963                 editable : false,
49964                 triggerAction: 'all',
49965                 emptyText:'Select Style',
49966                 selectOnFocus:true,
49967                 width: 130,
49968                 listeners : {
49969                     'select': function(c, r, i) {
49970                         // initial support only for on class per el..
49971                         tb.selectedNode.className =  r ? r.get('val') : '';
49972                         editorcore.syncValue();
49973                     }
49974                 }
49975     
49976             }));
49977         }
49978         
49979         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49980         
49981         
49982         for (var i = 0; i < tlist.length; i++) {
49983             
49984             // newer versions will use xtype cfg to create menus.
49985             if (typeof(tlist[i].xtype) != 'undefined') {
49986                 
49987                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49988                 
49989                 
49990                 continue;
49991             }
49992             
49993             var item = tlist[i];
49994             tb.add(item.title + ":&nbsp;");
49995             
49996             
49997             //optname == used so you can configure the options available..
49998             var opts = item.opts ? item.opts : false;
49999             if (item.optname) { // use the b
50000                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50001            
50002             }
50003             
50004             if (opts) {
50005                 // opts == pulldown..
50006                 tb.addField( new Roo.form.ComboBox({
50007                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50008                         id : 'val',
50009                         fields: ['val', 'display'],
50010                         data : opts  
50011                     }),
50012                     name : '-roo-edit-' + tlist[i].name,
50013                     
50014                     attrname : tlist[i].name,
50015                     stylename : item.style ? item.style : false,
50016                     
50017                     displayField: item.displayField ? item.displayField : 'val',
50018                     valueField :  'val',
50019                     typeAhead: false,
50020                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50021                     editable : false,
50022                     triggerAction: 'all',
50023                     emptyText:'Select',
50024                     selectOnFocus:true,
50025                     width: item.width ? item.width  : 130,
50026                     listeners : {
50027                         'select': function(c, r, i) {
50028                             if (tb.selectedNode.hasAttribute('data-block')) {
50029                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50030                                 b[c.attrname] = r.get('val');
50031                                 b.updateElement(tb.selectedNode);
50032                                 editorcore.syncValue();
50033                                 return;
50034                             }
50035                             
50036                             if (c.stylename) {
50037                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50038                                 editorcore.syncValue();
50039                                 return;
50040                             }
50041                             if (r === false) {
50042                                 tb.selectedNode.removeAttribute(c.attrname);
50043                                 editorcore.syncValue();
50044                                 return;
50045                             }
50046                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50047                             editorcore.syncValue();
50048                         }
50049                     }
50050
50051                 }));
50052                 continue;
50053                     
50054                  
50055                 /*
50056                 tb.addField( new Roo.form.TextField({
50057                     name: i,
50058                     width: 100,
50059                     //allowBlank:false,
50060                     value: ''
50061                 }));
50062                 continue;
50063                 */
50064             }
50065             tb.addField( new Roo.form.TextField({
50066                 name: '-roo-edit-' + tlist[i].name,
50067                 attrname : tlist[i].name,
50068                 
50069                 width: item.width,
50070                 //allowBlank:true,
50071                 value: '',
50072                 listeners: {
50073                     'change' : function(f, nv, ov) {
50074                         
50075                         if (tb.selectedNode.hasAttribute('data-block')) {
50076                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50077                             b[f.attrname] = nv;
50078                             b.updateElement(tb.selectedNode);
50079                             editorcore.syncValue();
50080                             return;
50081                         }
50082                         
50083                         tb.selectedNode.setAttribute(f.attrname, nv);
50084                         editorcore.syncValue();
50085                     }
50086                 }
50087             }));
50088              
50089         }
50090         
50091         var _this = this;
50092         
50093         if(nm == 'BODY'){
50094             tb.addSeparator();
50095         
50096             tb.addButton( {
50097                 text: 'Stylesheets',
50098
50099                 listeners : {
50100                     click : function ()
50101                     {
50102                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50103                     }
50104                 }
50105             });
50106         }
50107         
50108         tb.addFill();
50109         tb.addButton({
50110             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50111     
50112             listeners : {
50113                 click : function ()
50114                 {
50115                     var sn = tb.selectedNode;
50116                     if (block) {
50117                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50118                         
50119                     }
50120                     if (!sn) {
50121                         return;
50122                     }
50123                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50124                     if (sn.hasAttribute('data-block')) {
50125                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50126                         sn.parentNode.removeChild(sn);
50127                         
50128                     } else if (sn && sn.tagName != 'BODY') {
50129                         // remove and keep parents.
50130                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50131                         a.removeTag(sn);
50132                     }
50133                     
50134                     
50135                     var range = editorcore.createRange();
50136         
50137                     range.setStart(stn,0);
50138                     range.setEnd(stn,0); 
50139                     var selection = editorcore.getSelection();
50140                     selection.removeAllRanges();
50141                     selection.addRange(range);
50142                     
50143                     
50144                     //_this.updateToolbar(null, null, pn);
50145                     _this.updateToolbar(null, null, null);
50146                     _this.updateFooter(false);
50147                     
50148                 }
50149             }
50150             
50151                     
50152                 
50153             
50154         });
50155         
50156         
50157         tb.el.on('click', function(e){
50158             e.preventDefault(); // what does this do?
50159         });
50160         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50161         tb.el.hide();
50162         
50163         // dont need to disable them... as they will get hidden
50164         return tb;
50165          
50166         
50167     },
50168     buildFooter : function()
50169     {
50170         
50171         var fel = this.editor.wrap.createChild();
50172         this.footer = new Roo.Toolbar(fel);
50173         // toolbar has scrolly on left / right?
50174         var footDisp= new Roo.Toolbar.Fill();
50175         var _t = this;
50176         this.footer.add(
50177             {
50178                 text : '&lt;',
50179                 xtype: 'Button',
50180                 handler : function() {
50181                     _t.footDisp.scrollTo('left',0,true)
50182                 }
50183             }
50184         );
50185         this.footer.add( footDisp );
50186         this.footer.add( 
50187             {
50188                 text : '&gt;',
50189                 xtype: 'Button',
50190                 handler : function() {
50191                     // no animation..
50192                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50193                 }
50194             }
50195         );
50196         var fel = Roo.get(footDisp.el);
50197         fel.addClass('x-editor-context');
50198         this.footDispWrap = fel; 
50199         this.footDispWrap.overflow  = 'hidden';
50200         
50201         this.footDisp = fel.createChild();
50202         this.footDispWrap.on('click', this.onContextClick, this)
50203         
50204         
50205     },
50206     // when the footer contect changes
50207     onContextClick : function (ev,dom)
50208     {
50209         ev.preventDefault();
50210         var  cn = dom.className;
50211         //Roo.log(cn);
50212         if (!cn.match(/x-ed-loc-/)) {
50213             return;
50214         }
50215         var n = cn.split('-').pop();
50216         var ans = this.footerEls;
50217         var sel = ans[n];
50218         
50219         this.editorcore.selectNode(sel);
50220         
50221         
50222         this.updateToolbar(null, null, sel);
50223         
50224         
50225     }
50226     
50227     
50228     
50229     
50230     
50231 });
50232
50233
50234
50235
50236
50237 /*
50238  * Based on:
50239  * Ext JS Library 1.1.1
50240  * Copyright(c) 2006-2007, Ext JS, LLC.
50241  *
50242  * Originally Released Under LGPL - original licence link has changed is not relivant.
50243  *
50244  * Fork - LGPL
50245  * <script type="text/javascript">
50246  */
50247  
50248 /**
50249  * @class Roo.form.BasicForm
50250  * @extends Roo.util.Observable
50251  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50252  * @constructor
50253  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50254  * @param {Object} config Configuration options
50255  */
50256 Roo.form.BasicForm = function(el, config){
50257     this.allItems = [];
50258     this.childForms = [];
50259     Roo.apply(this, config);
50260     /*
50261      * The Roo.form.Field items in this form.
50262      * @type MixedCollection
50263      */
50264      
50265      
50266     this.items = new Roo.util.MixedCollection(false, function(o){
50267         return o.id || (o.id = Roo.id());
50268     });
50269     this.addEvents({
50270         /**
50271          * @event beforeaction
50272          * Fires before any action is performed. Return false to cancel the action.
50273          * @param {Form} this
50274          * @param {Action} action The action to be performed
50275          */
50276         beforeaction: true,
50277         /**
50278          * @event actionfailed
50279          * Fires when an action fails.
50280          * @param {Form} this
50281          * @param {Action} action The action that failed
50282          */
50283         actionfailed : true,
50284         /**
50285          * @event actioncomplete
50286          * Fires when an action is completed.
50287          * @param {Form} this
50288          * @param {Action} action The action that completed
50289          */
50290         actioncomplete : true
50291     });
50292     if(el){
50293         this.initEl(el);
50294     }
50295     Roo.form.BasicForm.superclass.constructor.call(this);
50296     
50297     Roo.form.BasicForm.popover.apply();
50298 };
50299
50300 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50301     /**
50302      * @cfg {String} method
50303      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50304      */
50305     /**
50306      * @cfg {DataReader} reader
50307      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50308      * This is optional as there is built-in support for processing JSON.
50309      */
50310     /**
50311      * @cfg {DataReader} errorReader
50312      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50313      * This is completely optional as there is built-in support for processing JSON.
50314      */
50315     /**
50316      * @cfg {String} url
50317      * The URL to use for form actions if one isn't supplied in the action options.
50318      */
50319     /**
50320      * @cfg {Boolean} fileUpload
50321      * Set to true if this form is a file upload.
50322      */
50323      
50324     /**
50325      * @cfg {Object} baseParams
50326      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50327      */
50328      /**
50329      
50330     /**
50331      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50332      */
50333     timeout: 30,
50334
50335     // private
50336     activeAction : null,
50337
50338     /**
50339      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50340      * or setValues() data instead of when the form was first created.
50341      */
50342     trackResetOnLoad : false,
50343     
50344     
50345     /**
50346      * childForms - used for multi-tab forms
50347      * @type {Array}
50348      */
50349     childForms : false,
50350     
50351     /**
50352      * allItems - full list of fields.
50353      * @type {Array}
50354      */
50355     allItems : false,
50356     
50357     /**
50358      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50359      * element by passing it or its id or mask the form itself by passing in true.
50360      * @type Mixed
50361      */
50362     waitMsgTarget : false,
50363     
50364     /**
50365      * @type Boolean
50366      */
50367     disableMask : false,
50368     
50369     /**
50370      * @cfg {Boolean} errorMask (true|false) default false
50371      */
50372     errorMask : false,
50373     
50374     /**
50375      * @cfg {Number} maskOffset Default 100
50376      */
50377     maskOffset : 100,
50378
50379     // private
50380     initEl : function(el){
50381         this.el = Roo.get(el);
50382         this.id = this.el.id || Roo.id();
50383         this.el.on('submit', this.onSubmit, this);
50384         this.el.addClass('x-form');
50385     },
50386
50387     // private
50388     onSubmit : function(e){
50389         e.stopEvent();
50390     },
50391
50392     /**
50393      * Returns true if client-side validation on the form is successful.
50394      * @return Boolean
50395      */
50396     isValid : function(){
50397         var valid = true;
50398         var target = false;
50399         this.items.each(function(f){
50400             if(f.validate()){
50401                 return;
50402             }
50403             
50404             valid = false;
50405                 
50406             if(!target && f.el.isVisible(true)){
50407                 target = f;
50408             }
50409         });
50410         
50411         if(this.errorMask && !valid){
50412             Roo.form.BasicForm.popover.mask(this, target);
50413         }
50414         
50415         return valid;
50416     },
50417     /**
50418      * Returns array of invalid form fields.
50419      * @return Array
50420      */
50421     
50422     invalidFields : function()
50423     {
50424         var ret = [];
50425         this.items.each(function(f){
50426             if(f.validate()){
50427                 return;
50428             }
50429             ret.push(f);
50430             
50431         });
50432         
50433         return ret;
50434     },
50435     
50436     
50437     /**
50438      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50439      * @return Boolean
50440      */
50441     isDirty : function(){
50442         var dirty = false;
50443         this.items.each(function(f){
50444            if(f.isDirty()){
50445                dirty = true;
50446                return false;
50447            }
50448         });
50449         return dirty;
50450     },
50451     
50452     /**
50453      * Returns true if any fields in this form have changed since their original load. (New version)
50454      * @return Boolean
50455      */
50456     
50457     hasChanged : function()
50458     {
50459         var dirty = false;
50460         this.items.each(function(f){
50461            if(f.hasChanged()){
50462                dirty = true;
50463                return false;
50464            }
50465         });
50466         return dirty;
50467         
50468     },
50469     /**
50470      * Resets all hasChanged to 'false' -
50471      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50472      * So hasChanged storage is only to be used for this purpose
50473      * @return Boolean
50474      */
50475     resetHasChanged : function()
50476     {
50477         this.items.each(function(f){
50478            f.resetHasChanged();
50479         });
50480         
50481     },
50482     
50483     
50484     /**
50485      * Performs a predefined action (submit or load) or custom actions you define on this form.
50486      * @param {String} actionName The name of the action type
50487      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50488      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50489      * accept other config options):
50490      * <pre>
50491 Property          Type             Description
50492 ----------------  ---------------  ----------------------------------------------------------------------------------
50493 url               String           The url for the action (defaults to the form's url)
50494 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50495 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50496 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50497                                    validate the form on the client (defaults to false)
50498      * </pre>
50499      * @return {BasicForm} this
50500      */
50501     doAction : function(action, options){
50502         if(typeof action == 'string'){
50503             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50504         }
50505         if(this.fireEvent('beforeaction', this, action) !== false){
50506             this.beforeAction(action);
50507             action.run.defer(100, action);
50508         }
50509         return this;
50510     },
50511
50512     /**
50513      * Shortcut to do a submit action.
50514      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50515      * @return {BasicForm} this
50516      */
50517     submit : function(options){
50518         this.doAction('submit', options);
50519         return this;
50520     },
50521
50522     /**
50523      * Shortcut to do a load action.
50524      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50525      * @return {BasicForm} this
50526      */
50527     load : function(options){
50528         this.doAction('load', options);
50529         return this;
50530     },
50531
50532     /**
50533      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50534      * @param {Record} record The record to edit
50535      * @return {BasicForm} this
50536      */
50537     updateRecord : function(record){
50538         record.beginEdit();
50539         var fs = record.fields;
50540         fs.each(function(f){
50541             var field = this.findField(f.name);
50542             if(field){
50543                 record.set(f.name, field.getValue());
50544             }
50545         }, this);
50546         record.endEdit();
50547         return this;
50548     },
50549
50550     /**
50551      * Loads an Roo.data.Record into this form.
50552      * @param {Record} record The record to load
50553      * @return {BasicForm} this
50554      */
50555     loadRecord : function(record){
50556         this.setValues(record.data);
50557         return this;
50558     },
50559
50560     // private
50561     beforeAction : function(action){
50562         var o = action.options;
50563         
50564         if(!this.disableMask) {
50565             if(this.waitMsgTarget === true){
50566                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50567             }else if(this.waitMsgTarget){
50568                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50569                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50570             }else {
50571                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50572             }
50573         }
50574         
50575          
50576     },
50577
50578     // private
50579     afterAction : function(action, success){
50580         this.activeAction = null;
50581         var o = action.options;
50582         
50583         if(!this.disableMask) {
50584             if(this.waitMsgTarget === true){
50585                 this.el.unmask();
50586             }else if(this.waitMsgTarget){
50587                 this.waitMsgTarget.unmask();
50588             }else{
50589                 Roo.MessageBox.updateProgress(1);
50590                 Roo.MessageBox.hide();
50591             }
50592         }
50593         
50594         if(success){
50595             if(o.reset){
50596                 this.reset();
50597             }
50598             Roo.callback(o.success, o.scope, [this, action]);
50599             this.fireEvent('actioncomplete', this, action);
50600             
50601         }else{
50602             
50603             // failure condition..
50604             // we have a scenario where updates need confirming.
50605             // eg. if a locking scenario exists..
50606             // we look for { errors : { needs_confirm : true }} in the response.
50607             if (
50608                 (typeof(action.result) != 'undefined')  &&
50609                 (typeof(action.result.errors) != 'undefined')  &&
50610                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50611            ){
50612                 var _t = this;
50613                 Roo.MessageBox.confirm(
50614                     "Change requires confirmation",
50615                     action.result.errorMsg,
50616                     function(r) {
50617                         if (r != 'yes') {
50618                             return;
50619                         }
50620                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50621                     }
50622                     
50623                 );
50624                 
50625                 
50626                 
50627                 return;
50628             }
50629             
50630             Roo.callback(o.failure, o.scope, [this, action]);
50631             // show an error message if no failed handler is set..
50632             if (!this.hasListener('actionfailed')) {
50633                 Roo.MessageBox.alert("Error",
50634                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50635                         action.result.errorMsg :
50636                         "Saving Failed, please check your entries or try again"
50637                 );
50638             }
50639             
50640             this.fireEvent('actionfailed', this, action);
50641         }
50642         
50643     },
50644
50645     /**
50646      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50647      * @param {String} id The value to search for
50648      * @return Field
50649      */
50650     findField : function(id){
50651         var field = this.items.get(id);
50652         if(!field){
50653             this.items.each(function(f){
50654                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50655                     field = f;
50656                     return false;
50657                 }
50658             });
50659         }
50660         return field || null;
50661     },
50662
50663     /**
50664      * Add a secondary form to this one, 
50665      * Used to provide tabbed forms. One form is primary, with hidden values 
50666      * which mirror the elements from the other forms.
50667      * 
50668      * @param {Roo.form.Form} form to add.
50669      * 
50670      */
50671     addForm : function(form)
50672     {
50673        
50674         if (this.childForms.indexOf(form) > -1) {
50675             // already added..
50676             return;
50677         }
50678         this.childForms.push(form);
50679         var n = '';
50680         Roo.each(form.allItems, function (fe) {
50681             
50682             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50683             if (this.findField(n)) { // already added..
50684                 return;
50685             }
50686             var add = new Roo.form.Hidden({
50687                 name : n
50688             });
50689             add.render(this.el);
50690             
50691             this.add( add );
50692         }, this);
50693         
50694     },
50695     /**
50696      * Mark fields in this form invalid in bulk.
50697      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50698      * @return {BasicForm} this
50699      */
50700     markInvalid : function(errors){
50701         if(errors instanceof Array){
50702             for(var i = 0, len = errors.length; i < len; i++){
50703                 var fieldError = errors[i];
50704                 var f = this.findField(fieldError.id);
50705                 if(f){
50706                     f.markInvalid(fieldError.msg);
50707                 }
50708             }
50709         }else{
50710             var field, id;
50711             for(id in errors){
50712                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50713                     field.markInvalid(errors[id]);
50714                 }
50715             }
50716         }
50717         Roo.each(this.childForms || [], function (f) {
50718             f.markInvalid(errors);
50719         });
50720         
50721         return this;
50722     },
50723
50724     /**
50725      * Set values for fields in this form in bulk.
50726      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50727      * @return {BasicForm} this
50728      */
50729     setValues : function(values){
50730         if(values instanceof Array){ // array of objects
50731             for(var i = 0, len = values.length; i < len; i++){
50732                 var v = values[i];
50733                 var f = this.findField(v.id);
50734                 if(f){
50735                     f.setValue(v.value);
50736                     if(this.trackResetOnLoad){
50737                         f.originalValue = f.getValue();
50738                     }
50739                 }
50740             }
50741         }else{ // object hash
50742             var field, id;
50743             for(id in values){
50744                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50745                     
50746                     if (field.setFromData && 
50747                         field.valueField && 
50748                         field.displayField &&
50749                         // combos' with local stores can 
50750                         // be queried via setValue()
50751                         // to set their value..
50752                         (field.store && !field.store.isLocal)
50753                         ) {
50754                         // it's a combo
50755                         var sd = { };
50756                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50757                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50758                         field.setFromData(sd);
50759                         
50760                     } else {
50761                         field.setValue(values[id]);
50762                     }
50763                     
50764                     
50765                     if(this.trackResetOnLoad){
50766                         field.originalValue = field.getValue();
50767                     }
50768                 }
50769             }
50770         }
50771         this.resetHasChanged();
50772         
50773         
50774         Roo.each(this.childForms || [], function (f) {
50775             f.setValues(values);
50776             f.resetHasChanged();
50777         });
50778                 
50779         return this;
50780     },
50781  
50782     /**
50783      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50784      * they are returned as an array.
50785      * @param {Boolean} asString
50786      * @return {Object}
50787      */
50788     getValues : function(asString)
50789     {
50790         if (this.childForms) {
50791             // copy values from the child forms
50792             Roo.each(this.childForms, function (f) {
50793                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50794             }, this);
50795         }
50796         
50797         // use formdata
50798         if (typeof(FormData) != 'undefined' && asString !== true) {
50799             // this relies on a 'recent' version of chrome apparently...
50800             try {
50801                 var fd = (new FormData(this.el.dom)).entries();
50802                 var ret = {};
50803                 var ent = fd.next();
50804                 while (!ent.done) {
50805                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50806                     ent = fd.next();
50807                 };
50808                 return ret;
50809             } catch(e) {
50810                 
50811             }
50812             
50813         }
50814         
50815         
50816         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50817         if(asString === true){
50818             return fs;
50819         }
50820         return Roo.urlDecode(fs);
50821     },
50822     
50823     /**
50824      * Returns the fields in this form as an object with key/value pairs. 
50825      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50826      * @return {Object}
50827      */
50828     getFieldValues : function(with_hidden)
50829     {
50830         if (this.childForms) {
50831             // copy values from the child forms
50832             // should this call getFieldValues - probably not as we do not currently copy
50833             // hidden fields when we generate..
50834             Roo.each(this.childForms, function (f) {
50835                 this.setValues(f.getFieldValues());
50836             }, this);
50837         }
50838         
50839         var ret = {};
50840         this.items.each(function(f){
50841             
50842             if (f.readOnly) {
50843                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50844                         // if a subform contains a copy of them.
50845                         // if you have subforms with the same editable data, you will need to copy the data back
50846                         // and forth.
50847             }
50848             
50849             if (!f.getName()) {
50850                 return;
50851             }
50852             var v = f.getValue();
50853             if (f.inputType =='radio') {
50854                 if (typeof(ret[f.getName()]) == 'undefined') {
50855                     ret[f.getName()] = ''; // empty..
50856                 }
50857                 
50858                 if (!f.el.dom.checked) {
50859                     return;
50860                     
50861                 }
50862                 v = f.el.dom.value;
50863                 
50864             }
50865             
50866             // not sure if this supported any more..
50867             if ((typeof(v) == 'object') && f.getRawValue) {
50868                 v = f.getRawValue() ; // dates..
50869             }
50870             // combo boxes where name != hiddenName...
50871             if (f.name != f.getName()) {
50872                 ret[f.name] = f.getRawValue();
50873             }
50874             ret[f.getName()] = v;
50875         });
50876         
50877         return ret;
50878     },
50879
50880     /**
50881      * Clears all invalid messages in this form.
50882      * @return {BasicForm} this
50883      */
50884     clearInvalid : function(){
50885         this.items.each(function(f){
50886            f.clearInvalid();
50887         });
50888         
50889         Roo.each(this.childForms || [], function (f) {
50890             f.clearInvalid();
50891         });
50892         
50893         
50894         return this;
50895     },
50896
50897     /**
50898      * Resets this form.
50899      * @return {BasicForm} this
50900      */
50901     reset : function(){
50902         this.items.each(function(f){
50903             f.reset();
50904         });
50905         
50906         Roo.each(this.childForms || [], function (f) {
50907             f.reset();
50908         });
50909         this.resetHasChanged();
50910         
50911         return this;
50912     },
50913
50914     /**
50915      * Add Roo.form components to this form.
50916      * @param {Field} field1
50917      * @param {Field} field2 (optional)
50918      * @param {Field} etc (optional)
50919      * @return {BasicForm} this
50920      */
50921     add : function(){
50922         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50923         return this;
50924     },
50925
50926
50927     /**
50928      * Removes a field from the items collection (does NOT remove its markup).
50929      * @param {Field} field
50930      * @return {BasicForm} this
50931      */
50932     remove : function(field){
50933         this.items.remove(field);
50934         return this;
50935     },
50936
50937     /**
50938      * Looks at the fields in this form, checks them for an id attribute,
50939      * and calls applyTo on the existing dom element with that id.
50940      * @return {BasicForm} this
50941      */
50942     render : function(){
50943         this.items.each(function(f){
50944             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50945                 f.applyTo(f.id);
50946             }
50947         });
50948         return this;
50949     },
50950
50951     /**
50952      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50953      * @param {Object} values
50954      * @return {BasicForm} this
50955      */
50956     applyToFields : function(o){
50957         this.items.each(function(f){
50958            Roo.apply(f, o);
50959         });
50960         return this;
50961     },
50962
50963     /**
50964      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50965      * @param {Object} values
50966      * @return {BasicForm} this
50967      */
50968     applyIfToFields : function(o){
50969         this.items.each(function(f){
50970            Roo.applyIf(f, o);
50971         });
50972         return this;
50973     }
50974 });
50975
50976 // back compat
50977 Roo.BasicForm = Roo.form.BasicForm;
50978
50979 Roo.apply(Roo.form.BasicForm, {
50980     
50981     popover : {
50982         
50983         padding : 5,
50984         
50985         isApplied : false,
50986         
50987         isMasked : false,
50988         
50989         form : false,
50990         
50991         target : false,
50992         
50993         intervalID : false,
50994         
50995         maskEl : false,
50996         
50997         apply : function()
50998         {
50999             if(this.isApplied){
51000                 return;
51001             }
51002             
51003             this.maskEl = {
51004                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51005                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51006                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51007                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51008             };
51009             
51010             this.maskEl.top.enableDisplayMode("block");
51011             this.maskEl.left.enableDisplayMode("block");
51012             this.maskEl.bottom.enableDisplayMode("block");
51013             this.maskEl.right.enableDisplayMode("block");
51014             
51015             Roo.get(document.body).on('click', function(){
51016                 this.unmask();
51017             }, this);
51018             
51019             Roo.get(document.body).on('touchstart', function(){
51020                 this.unmask();
51021             }, this);
51022             
51023             this.isApplied = true
51024         },
51025         
51026         mask : function(form, target)
51027         {
51028             this.form = form;
51029             
51030             this.target = target;
51031             
51032             if(!this.form.errorMask || !target.el){
51033                 return;
51034             }
51035             
51036             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51037             
51038             var ot = this.target.el.calcOffsetsTo(scrollable);
51039             
51040             var scrollTo = ot[1] - this.form.maskOffset;
51041             
51042             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51043             
51044             scrollable.scrollTo('top', scrollTo);
51045             
51046             var el = this.target.wrap || this.target.el;
51047             
51048             var box = el.getBox();
51049             
51050             this.maskEl.top.setStyle('position', 'absolute');
51051             this.maskEl.top.setStyle('z-index', 10000);
51052             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51053             this.maskEl.top.setLeft(0);
51054             this.maskEl.top.setTop(0);
51055             this.maskEl.top.show();
51056             
51057             this.maskEl.left.setStyle('position', 'absolute');
51058             this.maskEl.left.setStyle('z-index', 10000);
51059             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51060             this.maskEl.left.setLeft(0);
51061             this.maskEl.left.setTop(box.y - this.padding);
51062             this.maskEl.left.show();
51063
51064             this.maskEl.bottom.setStyle('position', 'absolute');
51065             this.maskEl.bottom.setStyle('z-index', 10000);
51066             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51067             this.maskEl.bottom.setLeft(0);
51068             this.maskEl.bottom.setTop(box.bottom + this.padding);
51069             this.maskEl.bottom.show();
51070
51071             this.maskEl.right.setStyle('position', 'absolute');
51072             this.maskEl.right.setStyle('z-index', 10000);
51073             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51074             this.maskEl.right.setLeft(box.right + this.padding);
51075             this.maskEl.right.setTop(box.y - this.padding);
51076             this.maskEl.right.show();
51077
51078             this.intervalID = window.setInterval(function() {
51079                 Roo.form.BasicForm.popover.unmask();
51080             }, 10000);
51081
51082             window.onwheel = function(){ return false;};
51083             
51084             (function(){ this.isMasked = true; }).defer(500, this);
51085             
51086         },
51087         
51088         unmask : function()
51089         {
51090             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51091                 return;
51092             }
51093             
51094             this.maskEl.top.setStyle('position', 'absolute');
51095             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51096             this.maskEl.top.hide();
51097
51098             this.maskEl.left.setStyle('position', 'absolute');
51099             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51100             this.maskEl.left.hide();
51101
51102             this.maskEl.bottom.setStyle('position', 'absolute');
51103             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51104             this.maskEl.bottom.hide();
51105
51106             this.maskEl.right.setStyle('position', 'absolute');
51107             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51108             this.maskEl.right.hide();
51109             
51110             window.onwheel = function(){ return true;};
51111             
51112             if(this.intervalID){
51113                 window.clearInterval(this.intervalID);
51114                 this.intervalID = false;
51115             }
51116             
51117             this.isMasked = false;
51118             
51119         }
51120         
51121     }
51122     
51123 });/*
51124  * Based on:
51125  * Ext JS Library 1.1.1
51126  * Copyright(c) 2006-2007, Ext JS, LLC.
51127  *
51128  * Originally Released Under LGPL - original licence link has changed is not relivant.
51129  *
51130  * Fork - LGPL
51131  * <script type="text/javascript">
51132  */
51133
51134 /**
51135  * @class Roo.form.Form
51136  * @extends Roo.form.BasicForm
51137  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51138  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51139  * @constructor
51140  * @param {Object} config Configuration options
51141  */
51142 Roo.form.Form = function(config){
51143     var xitems =  [];
51144     if (config.items) {
51145         xitems = config.items;
51146         delete config.items;
51147     }
51148    
51149     
51150     Roo.form.Form.superclass.constructor.call(this, null, config);
51151     this.url = this.url || this.action;
51152     if(!this.root){
51153         this.root = new Roo.form.Layout(Roo.applyIf({
51154             id: Roo.id()
51155         }, config));
51156     }
51157     this.active = this.root;
51158     /**
51159      * Array of all the buttons that have been added to this form via {@link addButton}
51160      * @type Array
51161      */
51162     this.buttons = [];
51163     this.allItems = [];
51164     this.addEvents({
51165         /**
51166          * @event clientvalidation
51167          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51168          * @param {Form} this
51169          * @param {Boolean} valid true if the form has passed client-side validation
51170          */
51171         clientvalidation: true,
51172         /**
51173          * @event rendered
51174          * Fires when the form is rendered
51175          * @param {Roo.form.Form} form
51176          */
51177         rendered : true
51178     });
51179     
51180     if (this.progressUrl) {
51181             // push a hidden field onto the list of fields..
51182             this.addxtype( {
51183                     xns: Roo.form, 
51184                     xtype : 'Hidden', 
51185                     name : 'UPLOAD_IDENTIFIER' 
51186             });
51187         }
51188         
51189     
51190     Roo.each(xitems, this.addxtype, this);
51191     
51192 };
51193
51194 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51195      /**
51196      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51197      */
51198     
51199     /**
51200      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51201      */
51202     /**
51203      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51204      */
51205     /**
51206      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51207      */
51208     buttonAlign:'center',
51209
51210     /**
51211      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51212      */
51213     minButtonWidth:75,
51214
51215     /**
51216      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51217      * This property cascades to child containers if not set.
51218      */
51219     labelAlign:'left',
51220
51221     /**
51222      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51223      * fires a looping event with that state. This is required to bind buttons to the valid
51224      * state using the config value formBind:true on the button.
51225      */
51226     monitorValid : false,
51227
51228     /**
51229      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51230      */
51231     monitorPoll : 200,
51232     
51233     /**
51234      * @cfg {String} progressUrl - Url to return progress data 
51235      */
51236     
51237     progressUrl : false,
51238     /**
51239      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51240      * sending a formdata with extra parameters - eg uploaded elements.
51241      */
51242     
51243     formData : false,
51244     
51245     /**
51246      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51247      * fields are added and the column is closed. If no fields are passed the column remains open
51248      * until end() is called.
51249      * @param {Object} config The config to pass to the column
51250      * @param {Field} field1 (optional)
51251      * @param {Field} field2 (optional)
51252      * @param {Field} etc (optional)
51253      * @return Column The column container object
51254      */
51255     column : function(c){
51256         var col = new Roo.form.Column(c);
51257         this.start(col);
51258         if(arguments.length > 1){ // duplicate code required because of Opera
51259             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51260             this.end();
51261         }
51262         return col;
51263     },
51264
51265     /**
51266      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51267      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51268      * until end() is called.
51269      * @param {Object} config The config to pass to the fieldset
51270      * @param {Field} field1 (optional)
51271      * @param {Field} field2 (optional)
51272      * @param {Field} etc (optional)
51273      * @return FieldSet The fieldset container object
51274      */
51275     fieldset : function(c){
51276         var fs = new Roo.form.FieldSet(c);
51277         this.start(fs);
51278         if(arguments.length > 1){ // duplicate code required because of Opera
51279             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51280             this.end();
51281         }
51282         return fs;
51283     },
51284
51285     /**
51286      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51287      * fields are added and the container is closed. If no fields are passed the container remains open
51288      * until end() is called.
51289      * @param {Object} config The config to pass to the Layout
51290      * @param {Field} field1 (optional)
51291      * @param {Field} field2 (optional)
51292      * @param {Field} etc (optional)
51293      * @return Layout The container object
51294      */
51295     container : function(c){
51296         var l = new Roo.form.Layout(c);
51297         this.start(l);
51298         if(arguments.length > 1){ // duplicate code required because of Opera
51299             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51300             this.end();
51301         }
51302         return l;
51303     },
51304
51305     /**
51306      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51307      * @param {Object} container A Roo.form.Layout or subclass of Layout
51308      * @return {Form} this
51309      */
51310     start : function(c){
51311         // cascade label info
51312         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51313         this.active.stack.push(c);
51314         c.ownerCt = this.active;
51315         this.active = c;
51316         return this;
51317     },
51318
51319     /**
51320      * Closes the current open container
51321      * @return {Form} this
51322      */
51323     end : function(){
51324         if(this.active == this.root){
51325             return this;
51326         }
51327         this.active = this.active.ownerCt;
51328         return this;
51329     },
51330
51331     /**
51332      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51333      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51334      * as the label of the field.
51335      * @param {Field} field1
51336      * @param {Field} field2 (optional)
51337      * @param {Field} etc. (optional)
51338      * @return {Form} this
51339      */
51340     add : function(){
51341         this.active.stack.push.apply(this.active.stack, arguments);
51342         this.allItems.push.apply(this.allItems,arguments);
51343         var r = [];
51344         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51345             if(a[i].isFormField){
51346                 r.push(a[i]);
51347             }
51348         }
51349         if(r.length > 0){
51350             Roo.form.Form.superclass.add.apply(this, r);
51351         }
51352         return this;
51353     },
51354     
51355
51356     
51357     
51358     
51359      /**
51360      * Find any element that has been added to a form, using it's ID or name
51361      * This can include framesets, columns etc. along with regular fields..
51362      * @param {String} id - id or name to find.
51363      
51364      * @return {Element} e - or false if nothing found.
51365      */
51366     findbyId : function(id)
51367     {
51368         var ret = false;
51369         if (!id) {
51370             return ret;
51371         }
51372         Roo.each(this.allItems, function(f){
51373             if (f.id == id || f.name == id ){
51374                 ret = f;
51375                 return false;
51376             }
51377         });
51378         return ret;
51379     },
51380
51381     
51382     
51383     /**
51384      * Render this form into the passed container. This should only be called once!
51385      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51386      * @return {Form} this
51387      */
51388     render : function(ct)
51389     {
51390         
51391         
51392         
51393         ct = Roo.get(ct);
51394         var o = this.autoCreate || {
51395             tag: 'form',
51396             method : this.method || 'POST',
51397             id : this.id || Roo.id()
51398         };
51399         this.initEl(ct.createChild(o));
51400
51401         this.root.render(this.el);
51402         
51403        
51404              
51405         this.items.each(function(f){
51406             f.render('x-form-el-'+f.id);
51407         });
51408
51409         if(this.buttons.length > 0){
51410             // tables are required to maintain order and for correct IE layout
51411             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51412                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51413                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51414             }}, null, true);
51415             var tr = tb.getElementsByTagName('tr')[0];
51416             for(var i = 0, len = this.buttons.length; i < len; i++) {
51417                 var b = this.buttons[i];
51418                 var td = document.createElement('td');
51419                 td.className = 'x-form-btn-td';
51420                 b.render(tr.appendChild(td));
51421             }
51422         }
51423         if(this.monitorValid){ // initialize after render
51424             this.startMonitoring();
51425         }
51426         this.fireEvent('rendered', this);
51427         return this;
51428     },
51429
51430     /**
51431      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51432      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51433      * object or a valid Roo.DomHelper element config
51434      * @param {Function} handler The function called when the button is clicked
51435      * @param {Object} scope (optional) The scope of the handler function
51436      * @return {Roo.Button}
51437      */
51438     addButton : function(config, handler, scope){
51439         var bc = {
51440             handler: handler,
51441             scope: scope,
51442             minWidth: this.minButtonWidth,
51443             hideParent:true
51444         };
51445         if(typeof config == "string"){
51446             bc.text = config;
51447         }else{
51448             Roo.apply(bc, config);
51449         }
51450         var btn = new Roo.Button(null, bc);
51451         this.buttons.push(btn);
51452         return btn;
51453     },
51454
51455      /**
51456      * Adds a series of form elements (using the xtype property as the factory method.
51457      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51458      * @param {Object} config 
51459      */
51460     
51461     addxtype : function()
51462     {
51463         var ar = Array.prototype.slice.call(arguments, 0);
51464         var ret = false;
51465         for(var i = 0; i < ar.length; i++) {
51466             if (!ar[i]) {
51467                 continue; // skip -- if this happends something invalid got sent, we 
51468                 // should ignore it, as basically that interface element will not show up
51469                 // and that should be pretty obvious!!
51470             }
51471             
51472             if (Roo.form[ar[i].xtype]) {
51473                 ar[i].form = this;
51474                 var fe = Roo.factory(ar[i], Roo.form);
51475                 if (!ret) {
51476                     ret = fe;
51477                 }
51478                 fe.form = this;
51479                 if (fe.store) {
51480                     fe.store.form = this;
51481                 }
51482                 if (fe.isLayout) {  
51483                          
51484                     this.start(fe);
51485                     this.allItems.push(fe);
51486                     if (fe.items && fe.addxtype) {
51487                         fe.addxtype.apply(fe, fe.items);
51488                         delete fe.items;
51489                     }
51490                      this.end();
51491                     continue;
51492                 }
51493                 
51494                 
51495                  
51496                 this.add(fe);
51497               //  console.log('adding ' + ar[i].xtype);
51498             }
51499             if (ar[i].xtype == 'Button') {  
51500                 //console.log('adding button');
51501                 //console.log(ar[i]);
51502                 this.addButton(ar[i]);
51503                 this.allItems.push(fe);
51504                 continue;
51505             }
51506             
51507             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51508                 alert('end is not supported on xtype any more, use items');
51509             //    this.end();
51510             //    //console.log('adding end');
51511             }
51512             
51513         }
51514         return ret;
51515     },
51516     
51517     /**
51518      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51519      * option "monitorValid"
51520      */
51521     startMonitoring : function(){
51522         if(!this.bound){
51523             this.bound = true;
51524             Roo.TaskMgr.start({
51525                 run : this.bindHandler,
51526                 interval : this.monitorPoll || 200,
51527                 scope: this
51528             });
51529         }
51530     },
51531
51532     /**
51533      * Stops monitoring of the valid state of this form
51534      */
51535     stopMonitoring : function(){
51536         this.bound = false;
51537     },
51538
51539     // private
51540     bindHandler : function(){
51541         if(!this.bound){
51542             return false; // stops binding
51543         }
51544         var valid = true;
51545         this.items.each(function(f){
51546             if(!f.isValid(true)){
51547                 valid = false;
51548                 return false;
51549             }
51550         });
51551         for(var i = 0, len = this.buttons.length; i < len; i++){
51552             var btn = this.buttons[i];
51553             if(btn.formBind === true && btn.disabled === valid){
51554                 btn.setDisabled(!valid);
51555             }
51556         }
51557         this.fireEvent('clientvalidation', this, valid);
51558     }
51559     
51560     
51561     
51562     
51563     
51564     
51565     
51566     
51567 });
51568
51569
51570 // back compat
51571 Roo.Form = Roo.form.Form;
51572 /*
51573  * Based on:
51574  * Ext JS Library 1.1.1
51575  * Copyright(c) 2006-2007, Ext JS, LLC.
51576  *
51577  * Originally Released Under LGPL - original licence link has changed is not relivant.
51578  *
51579  * Fork - LGPL
51580  * <script type="text/javascript">
51581  */
51582
51583 // as we use this in bootstrap.
51584 Roo.namespace('Roo.form');
51585  /**
51586  * @class Roo.form.Action
51587  * Internal Class used to handle form actions
51588  * @constructor
51589  * @param {Roo.form.BasicForm} el The form element or its id
51590  * @param {Object} config Configuration options
51591  */
51592
51593  
51594  
51595 // define the action interface
51596 Roo.form.Action = function(form, options){
51597     this.form = form;
51598     this.options = options || {};
51599 };
51600 /**
51601  * Client Validation Failed
51602  * @const 
51603  */
51604 Roo.form.Action.CLIENT_INVALID = 'client';
51605 /**
51606  * Server Validation Failed
51607  * @const 
51608  */
51609 Roo.form.Action.SERVER_INVALID = 'server';
51610  /**
51611  * Connect to Server Failed
51612  * @const 
51613  */
51614 Roo.form.Action.CONNECT_FAILURE = 'connect';
51615 /**
51616  * Reading Data from Server Failed
51617  * @const 
51618  */
51619 Roo.form.Action.LOAD_FAILURE = 'load';
51620
51621 Roo.form.Action.prototype = {
51622     type : 'default',
51623     failureType : undefined,
51624     response : undefined,
51625     result : undefined,
51626
51627     // interface method
51628     run : function(options){
51629
51630     },
51631
51632     // interface method
51633     success : function(response){
51634
51635     },
51636
51637     // interface method
51638     handleResponse : function(response){
51639
51640     },
51641
51642     // default connection failure
51643     failure : function(response){
51644         
51645         this.response = response;
51646         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51647         this.form.afterAction(this, false);
51648     },
51649
51650     processResponse : function(response){
51651         this.response = response;
51652         if(!response.responseText){
51653             return true;
51654         }
51655         this.result = this.handleResponse(response);
51656         return this.result;
51657     },
51658
51659     // utility functions used internally
51660     getUrl : function(appendParams){
51661         var url = this.options.url || this.form.url || this.form.el.dom.action;
51662         if(appendParams){
51663             var p = this.getParams();
51664             if(p){
51665                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51666             }
51667         }
51668         return url;
51669     },
51670
51671     getMethod : function(){
51672         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51673     },
51674
51675     getParams : function(){
51676         var bp = this.form.baseParams;
51677         var p = this.options.params;
51678         if(p){
51679             if(typeof p == "object"){
51680                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51681             }else if(typeof p == 'string' && bp){
51682                 p += '&' + Roo.urlEncode(bp);
51683             }
51684         }else if(bp){
51685             p = Roo.urlEncode(bp);
51686         }
51687         return p;
51688     },
51689
51690     createCallback : function(){
51691         return {
51692             success: this.success,
51693             failure: this.failure,
51694             scope: this,
51695             timeout: (this.form.timeout*1000),
51696             upload: this.form.fileUpload ? this.success : undefined
51697         };
51698     }
51699 };
51700
51701 Roo.form.Action.Submit = function(form, options){
51702     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51703 };
51704
51705 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51706     type : 'submit',
51707
51708     haveProgress : false,
51709     uploadComplete : false,
51710     
51711     // uploadProgress indicator.
51712     uploadProgress : function()
51713     {
51714         if (!this.form.progressUrl) {
51715             return;
51716         }
51717         
51718         if (!this.haveProgress) {
51719             Roo.MessageBox.progress("Uploading", "Uploading");
51720         }
51721         if (this.uploadComplete) {
51722            Roo.MessageBox.hide();
51723            return;
51724         }
51725         
51726         this.haveProgress = true;
51727    
51728         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51729         
51730         var c = new Roo.data.Connection();
51731         c.request({
51732             url : this.form.progressUrl,
51733             params: {
51734                 id : uid
51735             },
51736             method: 'GET',
51737             success : function(req){
51738                //console.log(data);
51739                 var rdata = false;
51740                 var edata;
51741                 try  {
51742                    rdata = Roo.decode(req.responseText)
51743                 } catch (e) {
51744                     Roo.log("Invalid data from server..");
51745                     Roo.log(edata);
51746                     return;
51747                 }
51748                 if (!rdata || !rdata.success) {
51749                     Roo.log(rdata);
51750                     Roo.MessageBox.alert(Roo.encode(rdata));
51751                     return;
51752                 }
51753                 var data = rdata.data;
51754                 
51755                 if (this.uploadComplete) {
51756                    Roo.MessageBox.hide();
51757                    return;
51758                 }
51759                    
51760                 if (data){
51761                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51762                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51763                     );
51764                 }
51765                 this.uploadProgress.defer(2000,this);
51766             },
51767        
51768             failure: function(data) {
51769                 Roo.log('progress url failed ');
51770                 Roo.log(data);
51771             },
51772             scope : this
51773         });
51774            
51775     },
51776     
51777     
51778     run : function()
51779     {
51780         // run get Values on the form, so it syncs any secondary forms.
51781         this.form.getValues();
51782         
51783         var o = this.options;
51784         var method = this.getMethod();
51785         var isPost = method == 'POST';
51786         if(o.clientValidation === false || this.form.isValid()){
51787             
51788             if (this.form.progressUrl) {
51789                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51790                     (new Date() * 1) + '' + Math.random());
51791                     
51792             } 
51793             
51794             
51795             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51796                 form:this.form.el.dom,
51797                 url:this.getUrl(!isPost),
51798                 method: method,
51799                 params:isPost ? this.getParams() : null,
51800                 isUpload: this.form.fileUpload,
51801                 formData : this.form.formData
51802             }));
51803             
51804             this.uploadProgress();
51805
51806         }else if (o.clientValidation !== false){ // client validation failed
51807             this.failureType = Roo.form.Action.CLIENT_INVALID;
51808             this.form.afterAction(this, false);
51809         }
51810     },
51811
51812     success : function(response)
51813     {
51814         this.uploadComplete= true;
51815         if (this.haveProgress) {
51816             Roo.MessageBox.hide();
51817         }
51818         
51819         
51820         var result = this.processResponse(response);
51821         if(result === true || result.success){
51822             this.form.afterAction(this, true);
51823             return;
51824         }
51825         if(result.errors){
51826             this.form.markInvalid(result.errors);
51827             this.failureType = Roo.form.Action.SERVER_INVALID;
51828         }
51829         this.form.afterAction(this, false);
51830     },
51831     failure : function(response)
51832     {
51833         this.uploadComplete= true;
51834         if (this.haveProgress) {
51835             Roo.MessageBox.hide();
51836         }
51837         
51838         this.response = response;
51839         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51840         this.form.afterAction(this, false);
51841     },
51842     
51843     handleResponse : function(response){
51844         if(this.form.errorReader){
51845             var rs = this.form.errorReader.read(response);
51846             var errors = [];
51847             if(rs.records){
51848                 for(var i = 0, len = rs.records.length; i < len; i++) {
51849                     var r = rs.records[i];
51850                     errors[i] = r.data;
51851                 }
51852             }
51853             if(errors.length < 1){
51854                 errors = null;
51855             }
51856             return {
51857                 success : rs.success,
51858                 errors : errors
51859             };
51860         }
51861         var ret = false;
51862         try {
51863             ret = Roo.decode(response.responseText);
51864         } catch (e) {
51865             ret = {
51866                 success: false,
51867                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51868                 errors : []
51869             };
51870         }
51871         return ret;
51872         
51873     }
51874 });
51875
51876
51877 Roo.form.Action.Load = function(form, options){
51878     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51879     this.reader = this.form.reader;
51880 };
51881
51882 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51883     type : 'load',
51884
51885     run : function(){
51886         
51887         Roo.Ajax.request(Roo.apply(
51888                 this.createCallback(), {
51889                     method:this.getMethod(),
51890                     url:this.getUrl(false),
51891                     params:this.getParams()
51892         }));
51893     },
51894
51895     success : function(response){
51896         
51897         var result = this.processResponse(response);
51898         if(result === true || !result.success || !result.data){
51899             this.failureType = Roo.form.Action.LOAD_FAILURE;
51900             this.form.afterAction(this, false);
51901             return;
51902         }
51903         this.form.clearInvalid();
51904         this.form.setValues(result.data);
51905         this.form.afterAction(this, true);
51906     },
51907
51908     handleResponse : function(response){
51909         if(this.form.reader){
51910             var rs = this.form.reader.read(response);
51911             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51912             return {
51913                 success : rs.success,
51914                 data : data
51915             };
51916         }
51917         return Roo.decode(response.responseText);
51918     }
51919 });
51920
51921 Roo.form.Action.ACTION_TYPES = {
51922     'load' : Roo.form.Action.Load,
51923     'submit' : Roo.form.Action.Submit
51924 };/*
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.form.Layout
51937  * @extends Roo.Component
51938  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51939  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51940  * @constructor
51941  * @param {Object} config Configuration options
51942  */
51943 Roo.form.Layout = function(config){
51944     var xitems = [];
51945     if (config.items) {
51946         xitems = config.items;
51947         delete config.items;
51948     }
51949     Roo.form.Layout.superclass.constructor.call(this, config);
51950     this.stack = [];
51951     Roo.each(xitems, this.addxtype, this);
51952      
51953 };
51954
51955 Roo.extend(Roo.form.Layout, Roo.Component, {
51956     /**
51957      * @cfg {String/Object} autoCreate
51958      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51959      */
51960     /**
51961      * @cfg {String/Object/Function} style
51962      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51963      * a function which returns such a specification.
51964      */
51965     /**
51966      * @cfg {String} labelAlign
51967      * Valid values are "left," "top" and "right" (defaults to "left")
51968      */
51969     /**
51970      * @cfg {Number} labelWidth
51971      * Fixed width in pixels of all field labels (defaults to undefined)
51972      */
51973     /**
51974      * @cfg {Boolean} clear
51975      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51976      */
51977     clear : true,
51978     /**
51979      * @cfg {String} labelSeparator
51980      * The separator to use after field labels (defaults to ':')
51981      */
51982     labelSeparator : ':',
51983     /**
51984      * @cfg {Boolean} hideLabels
51985      * True to suppress the display of field labels in this layout (defaults to false)
51986      */
51987     hideLabels : false,
51988
51989     // private
51990     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51991     
51992     isLayout : true,
51993     
51994     // private
51995     onRender : function(ct, position){
51996         if(this.el){ // from markup
51997             this.el = Roo.get(this.el);
51998         }else {  // generate
51999             var cfg = this.getAutoCreate();
52000             this.el = ct.createChild(cfg, position);
52001         }
52002         if(this.style){
52003             this.el.applyStyles(this.style);
52004         }
52005         if(this.labelAlign){
52006             this.el.addClass('x-form-label-'+this.labelAlign);
52007         }
52008         if(this.hideLabels){
52009             this.labelStyle = "display:none";
52010             this.elementStyle = "padding-left:0;";
52011         }else{
52012             if(typeof this.labelWidth == 'number'){
52013                 this.labelStyle = "width:"+this.labelWidth+"px;";
52014                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52015             }
52016             if(this.labelAlign == 'top'){
52017                 this.labelStyle = "width:auto;";
52018                 this.elementStyle = "padding-left:0;";
52019             }
52020         }
52021         var stack = this.stack;
52022         var slen = stack.length;
52023         if(slen > 0){
52024             if(!this.fieldTpl){
52025                 var t = new Roo.Template(
52026                     '<div class="x-form-item {5}">',
52027                         '<label for="{0}" style="{2}">{1}{4}</label>',
52028                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52029                         '</div>',
52030                     '</div><div class="x-form-clear-left"></div>'
52031                 );
52032                 t.disableFormats = true;
52033                 t.compile();
52034                 Roo.form.Layout.prototype.fieldTpl = t;
52035             }
52036             for(var i = 0; i < slen; i++) {
52037                 if(stack[i].isFormField){
52038                     this.renderField(stack[i]);
52039                 }else{
52040                     this.renderComponent(stack[i]);
52041                 }
52042             }
52043         }
52044         if(this.clear){
52045             this.el.createChild({cls:'x-form-clear'});
52046         }
52047     },
52048
52049     // private
52050     renderField : function(f){
52051         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52052                f.id, //0
52053                f.fieldLabel, //1
52054                f.labelStyle||this.labelStyle||'', //2
52055                this.elementStyle||'', //3
52056                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52057                f.itemCls||this.itemCls||''  //5
52058        ], true).getPrevSibling());
52059     },
52060
52061     // private
52062     renderComponent : function(c){
52063         c.render(c.isLayout ? this.el : this.el.createChild());    
52064     },
52065     /**
52066      * Adds a object form elements (using the xtype property as the factory method.)
52067      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52068      * @param {Object} config 
52069      */
52070     addxtype : function(o)
52071     {
52072         // create the lement.
52073         o.form = this.form;
52074         var fe = Roo.factory(o, Roo.form);
52075         this.form.allItems.push(fe);
52076         this.stack.push(fe);
52077         
52078         if (fe.isFormField) {
52079             this.form.items.add(fe);
52080         }
52081          
52082         return fe;
52083     }
52084 });
52085
52086 /**
52087  * @class Roo.form.Column
52088  * @extends Roo.form.Layout
52089  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52090  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52091  * @constructor
52092  * @param {Object} config Configuration options
52093  */
52094 Roo.form.Column = function(config){
52095     Roo.form.Column.superclass.constructor.call(this, config);
52096 };
52097
52098 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52099     /**
52100      * @cfg {Number/String} width
52101      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52102      */
52103     /**
52104      * @cfg {String/Object} autoCreate
52105      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52106      */
52107
52108     // private
52109     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52110
52111     // private
52112     onRender : function(ct, position){
52113         Roo.form.Column.superclass.onRender.call(this, ct, position);
52114         if(this.width){
52115             this.el.setWidth(this.width);
52116         }
52117     }
52118 });
52119
52120
52121 /**
52122  * @class Roo.form.Row
52123  * @extends Roo.form.Layout
52124  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52125  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52126  * @constructor
52127  * @param {Object} config Configuration options
52128  */
52129
52130  
52131 Roo.form.Row = function(config){
52132     Roo.form.Row.superclass.constructor.call(this, config);
52133 };
52134  
52135 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52136       /**
52137      * @cfg {Number/String} width
52138      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52139      */
52140     /**
52141      * @cfg {Number/String} height
52142      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52143      */
52144     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52145     
52146     padWidth : 20,
52147     // private
52148     onRender : function(ct, position){
52149         //console.log('row render');
52150         if(!this.rowTpl){
52151             var t = new Roo.Template(
52152                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52153                     '<label for="{0}" style="{2}">{1}{4}</label>',
52154                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52155                     '</div>',
52156                 '</div>'
52157             );
52158             t.disableFormats = true;
52159             t.compile();
52160             Roo.form.Layout.prototype.rowTpl = t;
52161         }
52162         this.fieldTpl = this.rowTpl;
52163         
52164         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52165         var labelWidth = 100;
52166         
52167         if ((this.labelAlign != 'top')) {
52168             if (typeof this.labelWidth == 'number') {
52169                 labelWidth = this.labelWidth
52170             }
52171             this.padWidth =  20 + labelWidth;
52172             
52173         }
52174         
52175         Roo.form.Column.superclass.onRender.call(this, ct, position);
52176         if(this.width){
52177             this.el.setWidth(this.width);
52178         }
52179         if(this.height){
52180             this.el.setHeight(this.height);
52181         }
52182     },
52183     
52184     // private
52185     renderField : function(f){
52186         f.fieldEl = this.fieldTpl.append(this.el, [
52187                f.id, f.fieldLabel,
52188                f.labelStyle||this.labelStyle||'',
52189                this.elementStyle||'',
52190                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52191                f.itemCls||this.itemCls||'',
52192                f.width ? f.width + this.padWidth : 160 + this.padWidth
52193        ],true);
52194     }
52195 });
52196  
52197
52198 /**
52199  * @class Roo.form.FieldSet
52200  * @extends Roo.form.Layout
52201  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52202  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52203  * @constructor
52204  * @param {Object} config Configuration options
52205  */
52206 Roo.form.FieldSet = function(config){
52207     Roo.form.FieldSet.superclass.constructor.call(this, config);
52208 };
52209
52210 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52211     /**
52212      * @cfg {String} legend
52213      * The text to display as the legend for the FieldSet (defaults to '')
52214      */
52215     /**
52216      * @cfg {String/Object} autoCreate
52217      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52218      */
52219
52220     // private
52221     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52222
52223     // private
52224     onRender : function(ct, position){
52225         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52226         if(this.legend){
52227             this.setLegend(this.legend);
52228         }
52229     },
52230
52231     // private
52232     setLegend : function(text){
52233         if(this.rendered){
52234             this.el.child('legend').update(text);
52235         }
52236     }
52237 });/*
52238  * Based on:
52239  * Ext JS Library 1.1.1
52240  * Copyright(c) 2006-2007, Ext JS, LLC.
52241  *
52242  * Originally Released Under LGPL - original licence link has changed is not relivant.
52243  *
52244  * Fork - LGPL
52245  * <script type="text/javascript">
52246  */
52247 /**
52248  * @class Roo.form.VTypes
52249  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52250  * @static
52251  */
52252 Roo.form.VTypes = function(){
52253     // closure these in so they are only created once.
52254     var alpha = /^[a-zA-Z_]+$/;
52255     var alphanum = /^[a-zA-Z0-9_]+$/;
52256     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52257     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52258
52259     // All these messages and functions are configurable
52260     return {
52261         /**
52262          * The function used to validate email addresses
52263          * @param {String} value The email address
52264          */
52265         'email' : function(v){
52266             return email.test(v);
52267         },
52268         /**
52269          * The error text to display when the email validation function returns false
52270          * @type String
52271          */
52272         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52273         /**
52274          * The keystroke filter mask to be applied on email input
52275          * @type RegExp
52276          */
52277         'emailMask' : /[a-z0-9_\.\-@]/i,
52278
52279         /**
52280          * The function used to validate URLs
52281          * @param {String} value The URL
52282          */
52283         'url' : function(v){
52284             return url.test(v);
52285         },
52286         /**
52287          * The error text to display when the url validation function returns false
52288          * @type String
52289          */
52290         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52291         
52292         /**
52293          * The function used to validate alpha values
52294          * @param {String} value The value
52295          */
52296         'alpha' : function(v){
52297             return alpha.test(v);
52298         },
52299         /**
52300          * The error text to display when the alpha validation function returns false
52301          * @type String
52302          */
52303         'alphaText' : 'This field should only contain letters and _',
52304         /**
52305          * The keystroke filter mask to be applied on alpha input
52306          * @type RegExp
52307          */
52308         'alphaMask' : /[a-z_]/i,
52309
52310         /**
52311          * The function used to validate alphanumeric values
52312          * @param {String} value The value
52313          */
52314         'alphanum' : function(v){
52315             return alphanum.test(v);
52316         },
52317         /**
52318          * The error text to display when the alphanumeric validation function returns false
52319          * @type String
52320          */
52321         'alphanumText' : 'This field should only contain letters, numbers and _',
52322         /**
52323          * The keystroke filter mask to be applied on alphanumeric input
52324          * @type RegExp
52325          */
52326         'alphanumMask' : /[a-z0-9_]/i
52327     };
52328 }();//<script type="text/javascript">
52329
52330 /**
52331  * @class Roo.form.FCKeditor
52332  * @extends Roo.form.TextArea
52333  * Wrapper around the FCKEditor http://www.fckeditor.net
52334  * @constructor
52335  * Creates a new FCKeditor
52336  * @param {Object} config Configuration options
52337  */
52338 Roo.form.FCKeditor = function(config){
52339     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52340     this.addEvents({
52341          /**
52342          * @event editorinit
52343          * Fired when the editor is initialized - you can add extra handlers here..
52344          * @param {FCKeditor} this
52345          * @param {Object} the FCK object.
52346          */
52347         editorinit : true
52348     });
52349     
52350     
52351 };
52352 Roo.form.FCKeditor.editors = { };
52353 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52354 {
52355     //defaultAutoCreate : {
52356     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52357     //},
52358     // private
52359     /**
52360      * @cfg {Object} fck options - see fck manual for details.
52361      */
52362     fckconfig : false,
52363     
52364     /**
52365      * @cfg {Object} fck toolbar set (Basic or Default)
52366      */
52367     toolbarSet : 'Basic',
52368     /**
52369      * @cfg {Object} fck BasePath
52370      */ 
52371     basePath : '/fckeditor/',
52372     
52373     
52374     frame : false,
52375     
52376     value : '',
52377     
52378    
52379     onRender : function(ct, position)
52380     {
52381         if(!this.el){
52382             this.defaultAutoCreate = {
52383                 tag: "textarea",
52384                 style:"width:300px;height:60px;",
52385                 autocomplete: "new-password"
52386             };
52387         }
52388         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52389         /*
52390         if(this.grow){
52391             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52392             if(this.preventScrollbars){
52393                 this.el.setStyle("overflow", "hidden");
52394             }
52395             this.el.setHeight(this.growMin);
52396         }
52397         */
52398         //console.log('onrender' + this.getId() );
52399         Roo.form.FCKeditor.editors[this.getId()] = this;
52400          
52401
52402         this.replaceTextarea() ;
52403         
52404     },
52405     
52406     getEditor : function() {
52407         return this.fckEditor;
52408     },
52409     /**
52410      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52411      * @param {Mixed} value The value to set
52412      */
52413     
52414     
52415     setValue : function(value)
52416     {
52417         //console.log('setValue: ' + value);
52418         
52419         if(typeof(value) == 'undefined') { // not sure why this is happending...
52420             return;
52421         }
52422         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52423         
52424         //if(!this.el || !this.getEditor()) {
52425         //    this.value = value;
52426             //this.setValue.defer(100,this,[value]);    
52427         //    return;
52428         //} 
52429         
52430         if(!this.getEditor()) {
52431             return;
52432         }
52433         
52434         this.getEditor().SetData(value);
52435         
52436         //
52437
52438     },
52439
52440     /**
52441      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52442      * @return {Mixed} value The field value
52443      */
52444     getValue : function()
52445     {
52446         
52447         if (this.frame && this.frame.dom.style.display == 'none') {
52448             return Roo.form.FCKeditor.superclass.getValue.call(this);
52449         }
52450         
52451         if(!this.el || !this.getEditor()) {
52452            
52453            // this.getValue.defer(100,this); 
52454             return this.value;
52455         }
52456        
52457         
52458         var value=this.getEditor().GetData();
52459         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52460         return Roo.form.FCKeditor.superclass.getValue.call(this);
52461         
52462
52463     },
52464
52465     /**
52466      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52467      * @return {Mixed} value The field value
52468      */
52469     getRawValue : function()
52470     {
52471         if (this.frame && this.frame.dom.style.display == 'none') {
52472             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52473         }
52474         
52475         if(!this.el || !this.getEditor()) {
52476             //this.getRawValue.defer(100,this); 
52477             return this.value;
52478             return;
52479         }
52480         
52481         
52482         
52483         var value=this.getEditor().GetData();
52484         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52485         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52486          
52487     },
52488     
52489     setSize : function(w,h) {
52490         
52491         
52492         
52493         //if (this.frame && this.frame.dom.style.display == 'none') {
52494         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52495         //    return;
52496         //}
52497         //if(!this.el || !this.getEditor()) {
52498         //    this.setSize.defer(100,this, [w,h]); 
52499         //    return;
52500         //}
52501         
52502         
52503         
52504         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52505         
52506         this.frame.dom.setAttribute('width', w);
52507         this.frame.dom.setAttribute('height', h);
52508         this.frame.setSize(w,h);
52509         
52510     },
52511     
52512     toggleSourceEdit : function(value) {
52513         
52514       
52515          
52516         this.el.dom.style.display = value ? '' : 'none';
52517         this.frame.dom.style.display = value ?  'none' : '';
52518         
52519     },
52520     
52521     
52522     focus: function(tag)
52523     {
52524         if (this.frame.dom.style.display == 'none') {
52525             return Roo.form.FCKeditor.superclass.focus.call(this);
52526         }
52527         if(!this.el || !this.getEditor()) {
52528             this.focus.defer(100,this, [tag]); 
52529             return;
52530         }
52531         
52532         
52533         
52534         
52535         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52536         this.getEditor().Focus();
52537         if (tgs.length) {
52538             if (!this.getEditor().Selection.GetSelection()) {
52539                 this.focus.defer(100,this, [tag]); 
52540                 return;
52541             }
52542             
52543             
52544             var r = this.getEditor().EditorDocument.createRange();
52545             r.setStart(tgs[0],0);
52546             r.setEnd(tgs[0],0);
52547             this.getEditor().Selection.GetSelection().removeAllRanges();
52548             this.getEditor().Selection.GetSelection().addRange(r);
52549             this.getEditor().Focus();
52550         }
52551         
52552     },
52553     
52554     
52555     
52556     replaceTextarea : function()
52557     {
52558         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52559             return ;
52560         }
52561         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52562         //{
52563             // We must check the elements firstly using the Id and then the name.
52564         var oTextarea = document.getElementById( this.getId() );
52565         
52566         var colElementsByName = document.getElementsByName( this.getId() ) ;
52567          
52568         oTextarea.style.display = 'none' ;
52569
52570         if ( oTextarea.tabIndex ) {            
52571             this.TabIndex = oTextarea.tabIndex ;
52572         }
52573         
52574         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52575         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52576         this.frame = Roo.get(this.getId() + '___Frame')
52577     },
52578     
52579     _getConfigHtml : function()
52580     {
52581         var sConfig = '' ;
52582
52583         for ( var o in this.fckconfig ) {
52584             sConfig += sConfig.length > 0  ? '&amp;' : '';
52585             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52586         }
52587
52588         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52589     },
52590     
52591     
52592     _getIFrameHtml : function()
52593     {
52594         var sFile = 'fckeditor.html' ;
52595         /* no idea what this is about..
52596         try
52597         {
52598             if ( (/fcksource=true/i).test( window.top.location.search ) )
52599                 sFile = 'fckeditor.original.html' ;
52600         }
52601         catch (e) { 
52602         */
52603
52604         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52605         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52606         
52607         
52608         var html = '<iframe id="' + this.getId() +
52609             '___Frame" src="' + sLink +
52610             '" width="' + this.width +
52611             '" height="' + this.height + '"' +
52612             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52613             ' frameborder="0" scrolling="no"></iframe>' ;
52614
52615         return html ;
52616     },
52617     
52618     _insertHtmlBefore : function( html, element )
52619     {
52620         if ( element.insertAdjacentHTML )       {
52621             // IE
52622             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52623         } else { // Gecko
52624             var oRange = document.createRange() ;
52625             oRange.setStartBefore( element ) ;
52626             var oFragment = oRange.createContextualFragment( html );
52627             element.parentNode.insertBefore( oFragment, element ) ;
52628         }
52629     }
52630     
52631     
52632   
52633     
52634     
52635     
52636     
52637
52638 });
52639
52640 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52641
52642 function FCKeditor_OnComplete(editorInstance){
52643     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52644     f.fckEditor = editorInstance;
52645     //console.log("loaded");
52646     f.fireEvent('editorinit', f, editorInstance);
52647
52648   
52649
52650  
52651
52652
52653
52654
52655
52656
52657
52658
52659
52660
52661
52662
52663
52664
52665
52666 //<script type="text/javascript">
52667 /**
52668  * @class Roo.form.GridField
52669  * @extends Roo.form.Field
52670  * Embed a grid (or editable grid into a form)
52671  * STATUS ALPHA
52672  * 
52673  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52674  * it needs 
52675  * xgrid.store = Roo.data.Store
52676  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52677  * xgrid.store.reader = Roo.data.JsonReader 
52678  * 
52679  * 
52680  * @constructor
52681  * Creates a new GridField
52682  * @param {Object} config Configuration options
52683  */
52684 Roo.form.GridField = function(config){
52685     Roo.form.GridField.superclass.constructor.call(this, config);
52686      
52687 };
52688
52689 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52690     /**
52691      * @cfg {Number} width  - used to restrict width of grid..
52692      */
52693     width : 100,
52694     /**
52695      * @cfg {Number} height - used to restrict height of grid..
52696      */
52697     height : 50,
52698      /**
52699      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52700          * 
52701          *}
52702      */
52703     xgrid : false, 
52704     /**
52705      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52706      * {tag: "input", type: "checkbox", autocomplete: "off"})
52707      */
52708    // defaultAutoCreate : { tag: 'div' },
52709     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52710     /**
52711      * @cfg {String} addTitle Text to include for adding a title.
52712      */
52713     addTitle : false,
52714     //
52715     onResize : function(){
52716         Roo.form.Field.superclass.onResize.apply(this, arguments);
52717     },
52718
52719     initEvents : function(){
52720         // Roo.form.Checkbox.superclass.initEvents.call(this);
52721         // has no events...
52722        
52723     },
52724
52725
52726     getResizeEl : function(){
52727         return this.wrap;
52728     },
52729
52730     getPositionEl : function(){
52731         return this.wrap;
52732     },
52733
52734     // private
52735     onRender : function(ct, position){
52736         
52737         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52738         var style = this.style;
52739         delete this.style;
52740         
52741         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52742         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52743         this.viewEl = this.wrap.createChild({ tag: 'div' });
52744         if (style) {
52745             this.viewEl.applyStyles(style);
52746         }
52747         if (this.width) {
52748             this.viewEl.setWidth(this.width);
52749         }
52750         if (this.height) {
52751             this.viewEl.setHeight(this.height);
52752         }
52753         //if(this.inputValue !== undefined){
52754         //this.setValue(this.value);
52755         
52756         
52757         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52758         
52759         
52760         this.grid.render();
52761         this.grid.getDataSource().on('remove', this.refreshValue, this);
52762         this.grid.getDataSource().on('update', this.refreshValue, this);
52763         this.grid.on('afteredit', this.refreshValue, this);
52764  
52765     },
52766      
52767     
52768     /**
52769      * Sets the value of the item. 
52770      * @param {String} either an object  or a string..
52771      */
52772     setValue : function(v){
52773         //this.value = v;
52774         v = v || []; // empty set..
52775         // this does not seem smart - it really only affects memoryproxy grids..
52776         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52777             var ds = this.grid.getDataSource();
52778             // assumes a json reader..
52779             var data = {}
52780             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52781             ds.loadData( data);
52782         }
52783         // clear selection so it does not get stale.
52784         if (this.grid.sm) { 
52785             this.grid.sm.clearSelections();
52786         }
52787         
52788         Roo.form.GridField.superclass.setValue.call(this, v);
52789         this.refreshValue();
52790         // should load data in the grid really....
52791     },
52792     
52793     // private
52794     refreshValue: function() {
52795          var val = [];
52796         this.grid.getDataSource().each(function(r) {
52797             val.push(r.data);
52798         });
52799         this.el.dom.value = Roo.encode(val);
52800     }
52801     
52802      
52803     
52804     
52805 });/*
52806  * Based on:
52807  * Ext JS Library 1.1.1
52808  * Copyright(c) 2006-2007, Ext JS, LLC.
52809  *
52810  * Originally Released Under LGPL - original licence link has changed is not relivant.
52811  *
52812  * Fork - LGPL
52813  * <script type="text/javascript">
52814  */
52815 /**
52816  * @class Roo.form.DisplayField
52817  * @extends Roo.form.Field
52818  * A generic Field to display non-editable data.
52819  * @cfg {Boolean} closable (true|false) default false
52820  * @constructor
52821  * Creates a new Display Field item.
52822  * @param {Object} config Configuration options
52823  */
52824 Roo.form.DisplayField = function(config){
52825     Roo.form.DisplayField.superclass.constructor.call(this, config);
52826     
52827     this.addEvents({
52828         /**
52829          * @event close
52830          * Fires after the click the close btn
52831              * @param {Roo.form.DisplayField} this
52832              */
52833         close : true
52834     });
52835 };
52836
52837 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52838     inputType:      'hidden',
52839     allowBlank:     true,
52840     readOnly:         true,
52841     
52842  
52843     /**
52844      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52845      */
52846     focusClass : undefined,
52847     /**
52848      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52849      */
52850     fieldClass: 'x-form-field',
52851     
52852      /**
52853      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52854      */
52855     valueRenderer: undefined,
52856     
52857     width: 100,
52858     /**
52859      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52860      * {tag: "input", type: "checkbox", autocomplete: "off"})
52861      */
52862      
52863  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52864  
52865     closable : false,
52866     
52867     onResize : function(){
52868         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52869         
52870     },
52871
52872     initEvents : function(){
52873         // Roo.form.Checkbox.superclass.initEvents.call(this);
52874         // has no events...
52875         
52876         if(this.closable){
52877             this.closeEl.on('click', this.onClose, this);
52878         }
52879        
52880     },
52881
52882
52883     getResizeEl : function(){
52884         return this.wrap;
52885     },
52886
52887     getPositionEl : function(){
52888         return this.wrap;
52889     },
52890
52891     // private
52892     onRender : function(ct, position){
52893         
52894         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52895         //if(this.inputValue !== undefined){
52896         this.wrap = this.el.wrap();
52897         
52898         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52899         
52900         if(this.closable){
52901             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52902         }
52903         
52904         if (this.bodyStyle) {
52905             this.viewEl.applyStyles(this.bodyStyle);
52906         }
52907         //this.viewEl.setStyle('padding', '2px');
52908         
52909         this.setValue(this.value);
52910         
52911     },
52912 /*
52913     // private
52914     initValue : Roo.emptyFn,
52915
52916   */
52917
52918         // private
52919     onClick : function(){
52920         
52921     },
52922
52923     /**
52924      * Sets the checked state of the checkbox.
52925      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52926      */
52927     setValue : function(v){
52928         this.value = v;
52929         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52930         // this might be called before we have a dom element..
52931         if (!this.viewEl) {
52932             return;
52933         }
52934         this.viewEl.dom.innerHTML = html;
52935         Roo.form.DisplayField.superclass.setValue.call(this, v);
52936
52937     },
52938     
52939     onClose : function(e)
52940     {
52941         e.preventDefault();
52942         
52943         this.fireEvent('close', this);
52944     }
52945 });/*
52946  * 
52947  * Licence- LGPL
52948  * 
52949  */
52950
52951 /**
52952  * @class Roo.form.DayPicker
52953  * @extends Roo.form.Field
52954  * A Day picker show [M] [T] [W] ....
52955  * @constructor
52956  * Creates a new Day Picker
52957  * @param {Object} config Configuration options
52958  */
52959 Roo.form.DayPicker= function(config){
52960     Roo.form.DayPicker.superclass.constructor.call(this, config);
52961      
52962 };
52963
52964 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52965     /**
52966      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52967      */
52968     focusClass : undefined,
52969     /**
52970      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52971      */
52972     fieldClass: "x-form-field",
52973    
52974     /**
52975      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52976      * {tag: "input", type: "checkbox", autocomplete: "off"})
52977      */
52978     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52979     
52980    
52981     actionMode : 'viewEl', 
52982     //
52983     // private
52984  
52985     inputType : 'hidden',
52986     
52987      
52988     inputElement: false, // real input element?
52989     basedOn: false, // ????
52990     
52991     isFormField: true, // not sure where this is needed!!!!
52992
52993     onResize : function(){
52994         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52995         if(!this.boxLabel){
52996             this.el.alignTo(this.wrap, 'c-c');
52997         }
52998     },
52999
53000     initEvents : function(){
53001         Roo.form.Checkbox.superclass.initEvents.call(this);
53002         this.el.on("click", this.onClick,  this);
53003         this.el.on("change", this.onClick,  this);
53004     },
53005
53006
53007     getResizeEl : function(){
53008         return this.wrap;
53009     },
53010
53011     getPositionEl : function(){
53012         return this.wrap;
53013     },
53014
53015     
53016     // private
53017     onRender : function(ct, position){
53018         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53019        
53020         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53021         
53022         var r1 = '<table><tr>';
53023         var r2 = '<tr class="x-form-daypick-icons">';
53024         for (var i=0; i < 7; i++) {
53025             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53026             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53027         }
53028         
53029         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53030         viewEl.select('img').on('click', this.onClick, this);
53031         this.viewEl = viewEl;   
53032         
53033         
53034         // this will not work on Chrome!!!
53035         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53036         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53037         
53038         
53039           
53040
53041     },
53042
53043     // private
53044     initValue : Roo.emptyFn,
53045
53046     /**
53047      * Returns the checked state of the checkbox.
53048      * @return {Boolean} True if checked, else false
53049      */
53050     getValue : function(){
53051         return this.el.dom.value;
53052         
53053     },
53054
53055         // private
53056     onClick : function(e){ 
53057         //this.setChecked(!this.checked);
53058         Roo.get(e.target).toggleClass('x-menu-item-checked');
53059         this.refreshValue();
53060         //if(this.el.dom.checked != this.checked){
53061         //    this.setValue(this.el.dom.checked);
53062        // }
53063     },
53064     
53065     // private
53066     refreshValue : function()
53067     {
53068         var val = '';
53069         this.viewEl.select('img',true).each(function(e,i,n)  {
53070             val += e.is(".x-menu-item-checked") ? String(n) : '';
53071         });
53072         this.setValue(val, true);
53073     },
53074
53075     /**
53076      * Sets the checked state of the checkbox.
53077      * On is always based on a string comparison between inputValue and the param.
53078      * @param {Boolean/String} value - the value to set 
53079      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53080      */
53081     setValue : function(v,suppressEvent){
53082         if (!this.el.dom) {
53083             return;
53084         }
53085         var old = this.el.dom.value ;
53086         this.el.dom.value = v;
53087         if (suppressEvent) {
53088             return ;
53089         }
53090          
53091         // update display..
53092         this.viewEl.select('img',true).each(function(e,i,n)  {
53093             
53094             var on = e.is(".x-menu-item-checked");
53095             var newv = v.indexOf(String(n)) > -1;
53096             if (on != newv) {
53097                 e.toggleClass('x-menu-item-checked');
53098             }
53099             
53100         });
53101         
53102         
53103         this.fireEvent('change', this, v, old);
53104         
53105         
53106     },
53107    
53108     // handle setting of hidden value by some other method!!?!?
53109     setFromHidden: function()
53110     {
53111         if(!this.el){
53112             return;
53113         }
53114         //console.log("SET FROM HIDDEN");
53115         //alert('setFrom hidden');
53116         this.setValue(this.el.dom.value);
53117     },
53118     
53119     onDestroy : function()
53120     {
53121         if(this.viewEl){
53122             Roo.get(this.viewEl).remove();
53123         }
53124          
53125         Roo.form.DayPicker.superclass.onDestroy.call(this);
53126     }
53127
53128 });/*
53129  * RooJS Library 1.1.1
53130  * Copyright(c) 2008-2011  Alan Knowles
53131  *
53132  * License - LGPL
53133  */
53134  
53135
53136 /**
53137  * @class Roo.form.ComboCheck
53138  * @extends Roo.form.ComboBox
53139  * A combobox for multiple select items.
53140  *
53141  * FIXME - could do with a reset button..
53142  * 
53143  * @constructor
53144  * Create a new ComboCheck
53145  * @param {Object} config Configuration options
53146  */
53147 Roo.form.ComboCheck = function(config){
53148     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53149     // should verify some data...
53150     // like
53151     // hiddenName = required..
53152     // displayField = required
53153     // valudField == required
53154     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53155     var _t = this;
53156     Roo.each(req, function(e) {
53157         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53158             throw "Roo.form.ComboCheck : missing value for: " + e;
53159         }
53160     });
53161     
53162     
53163 };
53164
53165 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53166      
53167      
53168     editable : false,
53169      
53170     selectedClass: 'x-menu-item-checked', 
53171     
53172     // private
53173     onRender : function(ct, position){
53174         var _t = this;
53175         
53176         
53177         
53178         if(!this.tpl){
53179             var cls = 'x-combo-list';
53180
53181             
53182             this.tpl =  new Roo.Template({
53183                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53184                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53185                    '<span>{' + this.displayField + '}</span>' +
53186                     '</div>' 
53187                 
53188             });
53189         }
53190  
53191         
53192         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53193         this.view.singleSelect = false;
53194         this.view.multiSelect = true;
53195         this.view.toggleSelect = true;
53196         this.pageTb.add(new Roo.Toolbar.Fill(), {
53197             
53198             text: 'Done',
53199             handler: function()
53200             {
53201                 _t.collapse();
53202             }
53203         });
53204     },
53205     
53206     onViewOver : function(e, t){
53207         // do nothing...
53208         return;
53209         
53210     },
53211     
53212     onViewClick : function(doFocus,index){
53213         return;
53214         
53215     },
53216     select: function () {
53217         //Roo.log("SELECT CALLED");
53218     },
53219      
53220     selectByValue : function(xv, scrollIntoView){
53221         var ar = this.getValueArray();
53222         var sels = [];
53223         
53224         Roo.each(ar, function(v) {
53225             if(v === undefined || v === null){
53226                 return;
53227             }
53228             var r = this.findRecord(this.valueField, v);
53229             if(r){
53230                 sels.push(this.store.indexOf(r))
53231                 
53232             }
53233         },this);
53234         this.view.select(sels);
53235         return false;
53236     },
53237     
53238     
53239     
53240     onSelect : function(record, index){
53241        // Roo.log("onselect Called");
53242        // this is only called by the clear button now..
53243         this.view.clearSelections();
53244         this.setValue('[]');
53245         if (this.value != this.valueBefore) {
53246             this.fireEvent('change', this, this.value, this.valueBefore);
53247             this.valueBefore = this.value;
53248         }
53249     },
53250     getValueArray : function()
53251     {
53252         var ar = [] ;
53253         
53254         try {
53255             //Roo.log(this.value);
53256             if (typeof(this.value) == 'undefined') {
53257                 return [];
53258             }
53259             var ar = Roo.decode(this.value);
53260             return  ar instanceof Array ? ar : []; //?? valid?
53261             
53262         } catch(e) {
53263             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53264             return [];
53265         }
53266          
53267     },
53268     expand : function ()
53269     {
53270         
53271         Roo.form.ComboCheck.superclass.expand.call(this);
53272         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53273         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53274         
53275
53276     },
53277     
53278     collapse : function(){
53279         Roo.form.ComboCheck.superclass.collapse.call(this);
53280         var sl = this.view.getSelectedIndexes();
53281         var st = this.store;
53282         var nv = [];
53283         var tv = [];
53284         var r;
53285         Roo.each(sl, function(i) {
53286             r = st.getAt(i);
53287             nv.push(r.get(this.valueField));
53288         },this);
53289         this.setValue(Roo.encode(nv));
53290         if (this.value != this.valueBefore) {
53291
53292             this.fireEvent('change', this, this.value, this.valueBefore);
53293             this.valueBefore = this.value;
53294         }
53295         
53296     },
53297     
53298     setValue : function(v){
53299         // Roo.log(v);
53300         this.value = v;
53301         
53302         var vals = this.getValueArray();
53303         var tv = [];
53304         Roo.each(vals, function(k) {
53305             var r = this.findRecord(this.valueField, k);
53306             if(r){
53307                 tv.push(r.data[this.displayField]);
53308             }else if(this.valueNotFoundText !== undefined){
53309                 tv.push( this.valueNotFoundText );
53310             }
53311         },this);
53312        // Roo.log(tv);
53313         
53314         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53315         this.hiddenField.value = v;
53316         this.value = v;
53317     }
53318     
53319 });/*
53320  * Based on:
53321  * Ext JS Library 1.1.1
53322  * Copyright(c) 2006-2007, Ext JS, LLC.
53323  *
53324  * Originally Released Under LGPL - original licence link has changed is not relivant.
53325  *
53326  * Fork - LGPL
53327  * <script type="text/javascript">
53328  */
53329  
53330 /**
53331  * @class Roo.form.Signature
53332  * @extends Roo.form.Field
53333  * Signature field.  
53334  * @constructor
53335  * 
53336  * @param {Object} config Configuration options
53337  */
53338
53339 Roo.form.Signature = function(config){
53340     Roo.form.Signature.superclass.constructor.call(this, config);
53341     
53342     this.addEvents({// not in used??
53343          /**
53344          * @event confirm
53345          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53346              * @param {Roo.form.Signature} combo This combo box
53347              */
53348         'confirm' : true,
53349         /**
53350          * @event reset
53351          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53352              * @param {Roo.form.ComboBox} combo This combo box
53353              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53354              */
53355         'reset' : true
53356     });
53357 };
53358
53359 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53360     /**
53361      * @cfg {Object} labels Label to use when rendering a form.
53362      * defaults to 
53363      * labels : { 
53364      *      clear : "Clear",
53365      *      confirm : "Confirm"
53366      *  }
53367      */
53368     labels : { 
53369         clear : "Clear",
53370         confirm : "Confirm"
53371     },
53372     /**
53373      * @cfg {Number} width The signature panel width (defaults to 300)
53374      */
53375     width: 300,
53376     /**
53377      * @cfg {Number} height The signature panel height (defaults to 100)
53378      */
53379     height : 100,
53380     /**
53381      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53382      */
53383     allowBlank : false,
53384     
53385     //private
53386     // {Object} signPanel The signature SVG panel element (defaults to {})
53387     signPanel : {},
53388     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53389     isMouseDown : false,
53390     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53391     isConfirmed : false,
53392     // {String} signatureTmp SVG mapping string (defaults to empty string)
53393     signatureTmp : '',
53394     
53395     
53396     defaultAutoCreate : { // modified by initCompnoent..
53397         tag: "input",
53398         type:"hidden"
53399     },
53400
53401     // private
53402     onRender : function(ct, position){
53403         
53404         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53405         
53406         this.wrap = this.el.wrap({
53407             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53408         });
53409         
53410         this.createToolbar(this);
53411         this.signPanel = this.wrap.createChild({
53412                 tag: 'div',
53413                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53414             }, this.el
53415         );
53416             
53417         this.svgID = Roo.id();
53418         this.svgEl = this.signPanel.createChild({
53419               xmlns : 'http://www.w3.org/2000/svg',
53420               tag : 'svg',
53421               id : this.svgID + "-svg",
53422               width: this.width,
53423               height: this.height,
53424               viewBox: '0 0 '+this.width+' '+this.height,
53425               cn : [
53426                 {
53427                     tag: "rect",
53428                     id: this.svgID + "-svg-r",
53429                     width: this.width,
53430                     height: this.height,
53431                     fill: "#ffa"
53432                 },
53433                 {
53434                     tag: "line",
53435                     id: this.svgID + "-svg-l",
53436                     x1: "0", // start
53437                     y1: (this.height*0.8), // start set the line in 80% of height
53438                     x2: this.width, // end
53439                     y2: (this.height*0.8), // end set the line in 80% of height
53440                     'stroke': "#666",
53441                     'stroke-width': "1",
53442                     'stroke-dasharray': "3",
53443                     'shape-rendering': "crispEdges",
53444                     'pointer-events': "none"
53445                 },
53446                 {
53447                     tag: "path",
53448                     id: this.svgID + "-svg-p",
53449                     'stroke': "navy",
53450                     'stroke-width': "3",
53451                     'fill': "none",
53452                     'pointer-events': 'none'
53453                 }
53454               ]
53455         });
53456         this.createSVG();
53457         this.svgBox = this.svgEl.dom.getScreenCTM();
53458     },
53459     createSVG : function(){ 
53460         var svg = this.signPanel;
53461         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53462         var t = this;
53463
53464         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53465         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53466         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53467         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53468         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53469         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53470         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53471         
53472     },
53473     isTouchEvent : function(e){
53474         return e.type.match(/^touch/);
53475     },
53476     getCoords : function (e) {
53477         var pt    = this.svgEl.dom.createSVGPoint();
53478         pt.x = e.clientX; 
53479         pt.y = e.clientY;
53480         if (this.isTouchEvent(e)) {
53481             pt.x =  e.targetTouches[0].clientX;
53482             pt.y = e.targetTouches[0].clientY;
53483         }
53484         var a = this.svgEl.dom.getScreenCTM();
53485         var b = a.inverse();
53486         var mx = pt.matrixTransform(b);
53487         return mx.x + ',' + mx.y;
53488     },
53489     //mouse event headler 
53490     down : function (e) {
53491         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53492         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53493         
53494         this.isMouseDown = true;
53495         
53496         e.preventDefault();
53497     },
53498     move : function (e) {
53499         if (this.isMouseDown) {
53500             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53501             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53502         }
53503         
53504         e.preventDefault();
53505     },
53506     up : function (e) {
53507         this.isMouseDown = false;
53508         var sp = this.signatureTmp.split(' ');
53509         
53510         if(sp.length > 1){
53511             if(!sp[sp.length-2].match(/^L/)){
53512                 sp.pop();
53513                 sp.pop();
53514                 sp.push("");
53515                 this.signatureTmp = sp.join(" ");
53516             }
53517         }
53518         if(this.getValue() != this.signatureTmp){
53519             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53520             this.isConfirmed = false;
53521         }
53522         e.preventDefault();
53523     },
53524     
53525     /**
53526      * Protected method that will not generally be called directly. It
53527      * is called when the editor creates its toolbar. Override this method if you need to
53528      * add custom toolbar buttons.
53529      * @param {HtmlEditor} editor
53530      */
53531     createToolbar : function(editor){
53532          function btn(id, toggle, handler){
53533             var xid = fid + '-'+ id ;
53534             return {
53535                 id : xid,
53536                 cmd : id,
53537                 cls : 'x-btn-icon x-edit-'+id,
53538                 enableToggle:toggle !== false,
53539                 scope: editor, // was editor...
53540                 handler:handler||editor.relayBtnCmd,
53541                 clickEvent:'mousedown',
53542                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53543                 tabIndex:-1
53544             };
53545         }
53546         
53547         
53548         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53549         this.tb = tb;
53550         this.tb.add(
53551            {
53552                 cls : ' x-signature-btn x-signature-'+id,
53553                 scope: editor, // was editor...
53554                 handler: this.reset,
53555                 clickEvent:'mousedown',
53556                 text: this.labels.clear
53557             },
53558             {
53559                  xtype : 'Fill',
53560                  xns: Roo.Toolbar
53561             }, 
53562             {
53563                 cls : '  x-signature-btn x-signature-'+id,
53564                 scope: editor, // was editor...
53565                 handler: this.confirmHandler,
53566                 clickEvent:'mousedown',
53567                 text: this.labels.confirm
53568             }
53569         );
53570     
53571     },
53572     //public
53573     /**
53574      * when user is clicked confirm then show this image.....
53575      * 
53576      * @return {String} Image Data URI
53577      */
53578     getImageDataURI : function(){
53579         var svg = this.svgEl.dom.parentNode.innerHTML;
53580         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53581         return src; 
53582     },
53583     /**
53584      * 
53585      * @return {Boolean} this.isConfirmed
53586      */
53587     getConfirmed : function(){
53588         return this.isConfirmed;
53589     },
53590     /**
53591      * 
53592      * @return {Number} this.width
53593      */
53594     getWidth : function(){
53595         return this.width;
53596     },
53597     /**
53598      * 
53599      * @return {Number} this.height
53600      */
53601     getHeight : function(){
53602         return this.height;
53603     },
53604     // private
53605     getSignature : function(){
53606         return this.signatureTmp;
53607     },
53608     // private
53609     reset : function(){
53610         this.signatureTmp = '';
53611         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53612         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53613         this.isConfirmed = false;
53614         Roo.form.Signature.superclass.reset.call(this);
53615     },
53616     setSignature : function(s){
53617         this.signatureTmp = s;
53618         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53619         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53620         this.setValue(s);
53621         this.isConfirmed = false;
53622         Roo.form.Signature.superclass.reset.call(this);
53623     }, 
53624     test : function(){
53625 //        Roo.log(this.signPanel.dom.contentWindow.up())
53626     },
53627     //private
53628     setConfirmed : function(){
53629         
53630         
53631         
53632 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53633     },
53634     // private
53635     confirmHandler : function(){
53636         if(!this.getSignature()){
53637             return;
53638         }
53639         
53640         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53641         this.setValue(this.getSignature());
53642         this.isConfirmed = true;
53643         
53644         this.fireEvent('confirm', this);
53645     },
53646     // private
53647     // Subclasses should provide the validation implementation by overriding this
53648     validateValue : function(value){
53649         if(this.allowBlank){
53650             return true;
53651         }
53652         
53653         if(this.isConfirmed){
53654             return true;
53655         }
53656         return false;
53657     }
53658 });/*
53659  * Based on:
53660  * Ext JS Library 1.1.1
53661  * Copyright(c) 2006-2007, Ext JS, LLC.
53662  *
53663  * Originally Released Under LGPL - original licence link has changed is not relivant.
53664  *
53665  * Fork - LGPL
53666  * <script type="text/javascript">
53667  */
53668  
53669
53670 /**
53671  * @class Roo.form.ComboBox
53672  * @extends Roo.form.TriggerField
53673  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53674  * @constructor
53675  * Create a new ComboBox.
53676  * @param {Object} config Configuration options
53677  */
53678 Roo.form.Select = function(config){
53679     Roo.form.Select.superclass.constructor.call(this, config);
53680      
53681 };
53682
53683 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53684     /**
53685      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53686      */
53687     /**
53688      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53689      * rendering into an Roo.Editor, defaults to false)
53690      */
53691     /**
53692      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53693      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53694      */
53695     /**
53696      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53697      */
53698     /**
53699      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53700      * the dropdown list (defaults to undefined, with no header element)
53701      */
53702
53703      /**
53704      * @cfg {String/Roo.Template} tpl The template to use to render the output
53705      */
53706      
53707     // private
53708     defaultAutoCreate : {tag: "select"  },
53709     /**
53710      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53711      */
53712     listWidth: undefined,
53713     /**
53714      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53715      * mode = 'remote' or 'text' if mode = 'local')
53716      */
53717     displayField: undefined,
53718     /**
53719      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53720      * mode = 'remote' or 'value' if mode = 'local'). 
53721      * Note: use of a valueField requires the user make a selection
53722      * in order for a value to be mapped.
53723      */
53724     valueField: undefined,
53725     
53726     
53727     /**
53728      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53729      * field's data value (defaults to the underlying DOM element's name)
53730      */
53731     hiddenName: undefined,
53732     /**
53733      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53734      */
53735     listClass: '',
53736     /**
53737      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53738      */
53739     selectedClass: 'x-combo-selected',
53740     /**
53741      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53742      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53743      * which displays a downward arrow icon).
53744      */
53745     triggerClass : 'x-form-arrow-trigger',
53746     /**
53747      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53748      */
53749     shadow:'sides',
53750     /**
53751      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53752      * anchor positions (defaults to 'tl-bl')
53753      */
53754     listAlign: 'tl-bl?',
53755     /**
53756      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53757      */
53758     maxHeight: 300,
53759     /**
53760      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53761      * query specified by the allQuery config option (defaults to 'query')
53762      */
53763     triggerAction: 'query',
53764     /**
53765      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53766      * (defaults to 4, does not apply if editable = false)
53767      */
53768     minChars : 4,
53769     /**
53770      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53771      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53772      */
53773     typeAhead: false,
53774     /**
53775      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53776      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53777      */
53778     queryDelay: 500,
53779     /**
53780      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53781      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53782      */
53783     pageSize: 0,
53784     /**
53785      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53786      * when editable = true (defaults to false)
53787      */
53788     selectOnFocus:false,
53789     /**
53790      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53791      */
53792     queryParam: 'query',
53793     /**
53794      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53795      * when mode = 'remote' (defaults to 'Loading...')
53796      */
53797     loadingText: 'Loading...',
53798     /**
53799      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53800      */
53801     resizable: false,
53802     /**
53803      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53804      */
53805     handleHeight : 8,
53806     /**
53807      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53808      * traditional select (defaults to true)
53809      */
53810     editable: true,
53811     /**
53812      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53813      */
53814     allQuery: '',
53815     /**
53816      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53817      */
53818     mode: 'remote',
53819     /**
53820      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53821      * listWidth has a higher value)
53822      */
53823     minListWidth : 70,
53824     /**
53825      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53826      * allow the user to set arbitrary text into the field (defaults to false)
53827      */
53828     forceSelection:false,
53829     /**
53830      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53831      * if typeAhead = true (defaults to 250)
53832      */
53833     typeAheadDelay : 250,
53834     /**
53835      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53836      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53837      */
53838     valueNotFoundText : undefined,
53839     
53840     /**
53841      * @cfg {String} defaultValue The value displayed after loading the store.
53842      */
53843     defaultValue: '',
53844     
53845     /**
53846      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53847      */
53848     blockFocus : false,
53849     
53850     /**
53851      * @cfg {Boolean} disableClear Disable showing of clear button.
53852      */
53853     disableClear : false,
53854     /**
53855      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53856      */
53857     alwaysQuery : false,
53858     
53859     //private
53860     addicon : false,
53861     editicon: false,
53862     
53863     // element that contains real text value.. (when hidden is used..)
53864      
53865     // private
53866     onRender : function(ct, position){
53867         Roo.form.Field.prototype.onRender.call(this, ct, position);
53868         
53869         if(this.store){
53870             this.store.on('beforeload', this.onBeforeLoad, this);
53871             this.store.on('load', this.onLoad, this);
53872             this.store.on('loadexception', this.onLoadException, this);
53873             this.store.load({});
53874         }
53875         
53876         
53877         
53878     },
53879
53880     // private
53881     initEvents : function(){
53882         //Roo.form.ComboBox.superclass.initEvents.call(this);
53883  
53884     },
53885
53886     onDestroy : function(){
53887        
53888         if(this.store){
53889             this.store.un('beforeload', this.onBeforeLoad, this);
53890             this.store.un('load', this.onLoad, this);
53891             this.store.un('loadexception', this.onLoadException, this);
53892         }
53893         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53894     },
53895
53896     // private
53897     fireKey : function(e){
53898         if(e.isNavKeyPress() && !this.list.isVisible()){
53899             this.fireEvent("specialkey", this, e);
53900         }
53901     },
53902
53903     // private
53904     onResize: function(w, h){
53905         
53906         return; 
53907     
53908         
53909     },
53910
53911     /**
53912      * Allow or prevent the user from directly editing the field text.  If false is passed,
53913      * the user will only be able to select from the items defined in the dropdown list.  This method
53914      * is the runtime equivalent of setting the 'editable' config option at config time.
53915      * @param {Boolean} value True to allow the user to directly edit the field text
53916      */
53917     setEditable : function(value){
53918          
53919     },
53920
53921     // private
53922     onBeforeLoad : function(){
53923         
53924         Roo.log("Select before load");
53925         return;
53926     
53927         this.innerList.update(this.loadingText ?
53928                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53929         //this.restrictHeight();
53930         this.selectedIndex = -1;
53931     },
53932
53933     // private
53934     onLoad : function(){
53935
53936     
53937         var dom = this.el.dom;
53938         dom.innerHTML = '';
53939          var od = dom.ownerDocument;
53940          
53941         if (this.emptyText) {
53942             var op = od.createElement('option');
53943             op.setAttribute('value', '');
53944             op.innerHTML = String.format('{0}', this.emptyText);
53945             dom.appendChild(op);
53946         }
53947         if(this.store.getCount() > 0){
53948            
53949             var vf = this.valueField;
53950             var df = this.displayField;
53951             this.store.data.each(function(r) {
53952                 // which colmsn to use... testing - cdoe / title..
53953                 var op = od.createElement('option');
53954                 op.setAttribute('value', r.data[vf]);
53955                 op.innerHTML = String.format('{0}', r.data[df]);
53956                 dom.appendChild(op);
53957             });
53958             if (typeof(this.defaultValue != 'undefined')) {
53959                 this.setValue(this.defaultValue);
53960             }
53961             
53962              
53963         }else{
53964             //this.onEmptyResults();
53965         }
53966         //this.el.focus();
53967     },
53968     // private
53969     onLoadException : function()
53970     {
53971         dom.innerHTML = '';
53972             
53973         Roo.log("Select on load exception");
53974         return;
53975     
53976         this.collapse();
53977         Roo.log(this.store.reader.jsonData);
53978         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53979             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53980         }
53981         
53982         
53983     },
53984     // private
53985     onTypeAhead : function(){
53986          
53987     },
53988
53989     // private
53990     onSelect : function(record, index){
53991         Roo.log('on select?');
53992         return;
53993         if(this.fireEvent('beforeselect', this, record, index) !== false){
53994             this.setFromData(index > -1 ? record.data : false);
53995             this.collapse();
53996             this.fireEvent('select', this, record, index);
53997         }
53998     },
53999
54000     /**
54001      * Returns the currently selected field value or empty string if no value is set.
54002      * @return {String} value The selected value
54003      */
54004     getValue : function(){
54005         var dom = this.el.dom;
54006         this.value = dom.options[dom.selectedIndex].value;
54007         return this.value;
54008         
54009     },
54010
54011     /**
54012      * Clears any text/value currently set in the field
54013      */
54014     clearValue : function(){
54015         this.value = '';
54016         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54017         
54018     },
54019
54020     /**
54021      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54022      * will be displayed in the field.  If the value does not match the data value of an existing item,
54023      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54024      * Otherwise the field will be blank (although the value will still be set).
54025      * @param {String} value The value to match
54026      */
54027     setValue : function(v){
54028         var d = this.el.dom;
54029         for (var i =0; i < d.options.length;i++) {
54030             if (v == d.options[i].value) {
54031                 d.selectedIndex = i;
54032                 this.value = v;
54033                 return;
54034             }
54035         }
54036         this.clearValue();
54037     },
54038     /**
54039      * @property {Object} the last set data for the element
54040      */
54041     
54042     lastData : false,
54043     /**
54044      * Sets the value of the field based on a object which is related to the record format for the store.
54045      * @param {Object} value the value to set as. or false on reset?
54046      */
54047     setFromData : function(o){
54048         Roo.log('setfrom data?');
54049          
54050         
54051         
54052     },
54053     // private
54054     reset : function(){
54055         this.clearValue();
54056     },
54057     // private
54058     findRecord : function(prop, value){
54059         
54060         return false;
54061     
54062         var record;
54063         if(this.store.getCount() > 0){
54064             this.store.each(function(r){
54065                 if(r.data[prop] == value){
54066                     record = r;
54067                     return false;
54068                 }
54069                 return true;
54070             });
54071         }
54072         return record;
54073     },
54074     
54075     getName: function()
54076     {
54077         // returns hidden if it's set..
54078         if (!this.rendered) {return ''};
54079         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54080         
54081     },
54082      
54083
54084     
54085
54086     // private
54087     onEmptyResults : function(){
54088         Roo.log('empty results');
54089         //this.collapse();
54090     },
54091
54092     /**
54093      * Returns true if the dropdown list is expanded, else false.
54094      */
54095     isExpanded : function(){
54096         return false;
54097     },
54098
54099     /**
54100      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54101      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54102      * @param {String} value The data value of the item to select
54103      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54104      * selected item if it is not currently in view (defaults to true)
54105      * @return {Boolean} True if the value matched an item in the list, else false
54106      */
54107     selectByValue : function(v, scrollIntoView){
54108         Roo.log('select By Value');
54109         return false;
54110     
54111         if(v !== undefined && v !== null){
54112             var r = this.findRecord(this.valueField || this.displayField, v);
54113             if(r){
54114                 this.select(this.store.indexOf(r), scrollIntoView);
54115                 return true;
54116             }
54117         }
54118         return false;
54119     },
54120
54121     /**
54122      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54123      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54124      * @param {Number} index The zero-based index of the list item to select
54125      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54126      * selected item if it is not currently in view (defaults to true)
54127      */
54128     select : function(index, scrollIntoView){
54129         Roo.log('select ');
54130         return  ;
54131         
54132         this.selectedIndex = index;
54133         this.view.select(index);
54134         if(scrollIntoView !== false){
54135             var el = this.view.getNode(index);
54136             if(el){
54137                 this.innerList.scrollChildIntoView(el, false);
54138             }
54139         }
54140     },
54141
54142       
54143
54144     // private
54145     validateBlur : function(){
54146         
54147         return;
54148         
54149     },
54150
54151     // private
54152     initQuery : function(){
54153         this.doQuery(this.getRawValue());
54154     },
54155
54156     // private
54157     doForce : function(){
54158         if(this.el.dom.value.length > 0){
54159             this.el.dom.value =
54160                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54161              
54162         }
54163     },
54164
54165     /**
54166      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54167      * query allowing the query action to be canceled if needed.
54168      * @param {String} query The SQL query to execute
54169      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54170      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54171      * saved in the current store (defaults to false)
54172      */
54173     doQuery : function(q, forceAll){
54174         
54175         Roo.log('doQuery?');
54176         if(q === undefined || q === null){
54177             q = '';
54178         }
54179         var qe = {
54180             query: q,
54181             forceAll: forceAll,
54182             combo: this,
54183             cancel:false
54184         };
54185         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54186             return false;
54187         }
54188         q = qe.query;
54189         forceAll = qe.forceAll;
54190         if(forceAll === true || (q.length >= this.minChars)){
54191             if(this.lastQuery != q || this.alwaysQuery){
54192                 this.lastQuery = q;
54193                 if(this.mode == 'local'){
54194                     this.selectedIndex = -1;
54195                     if(forceAll){
54196                         this.store.clearFilter();
54197                     }else{
54198                         this.store.filter(this.displayField, q);
54199                     }
54200                     this.onLoad();
54201                 }else{
54202                     this.store.baseParams[this.queryParam] = q;
54203                     this.store.load({
54204                         params: this.getParams(q)
54205                     });
54206                     this.expand();
54207                 }
54208             }else{
54209                 this.selectedIndex = -1;
54210                 this.onLoad();   
54211             }
54212         }
54213     },
54214
54215     // private
54216     getParams : function(q){
54217         var p = {};
54218         //p[this.queryParam] = q;
54219         if(this.pageSize){
54220             p.start = 0;
54221             p.limit = this.pageSize;
54222         }
54223         return p;
54224     },
54225
54226     /**
54227      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54228      */
54229     collapse : function(){
54230         
54231     },
54232
54233     // private
54234     collapseIf : function(e){
54235         
54236     },
54237
54238     /**
54239      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54240      */
54241     expand : function(){
54242         
54243     } ,
54244
54245     // private
54246      
54247
54248     /** 
54249     * @cfg {Boolean} grow 
54250     * @hide 
54251     */
54252     /** 
54253     * @cfg {Number} growMin 
54254     * @hide 
54255     */
54256     /** 
54257     * @cfg {Number} growMax 
54258     * @hide 
54259     */
54260     /**
54261      * @hide
54262      * @method autoSize
54263      */
54264     
54265     setWidth : function()
54266     {
54267         
54268     },
54269     getResizeEl : function(){
54270         return this.el;
54271     }
54272 });//<script type="text/javasscript">
54273  
54274
54275 /**
54276  * @class Roo.DDView
54277  * A DnD enabled version of Roo.View.
54278  * @param {Element/String} container The Element in which to create the View.
54279  * @param {String} tpl The template string used to create the markup for each element of the View
54280  * @param {Object} config The configuration properties. These include all the config options of
54281  * {@link Roo.View} plus some specific to this class.<br>
54282  * <p>
54283  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54284  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54285  * <p>
54286  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54287 .x-view-drag-insert-above {
54288         border-top:1px dotted #3366cc;
54289 }
54290 .x-view-drag-insert-below {
54291         border-bottom:1px dotted #3366cc;
54292 }
54293 </code></pre>
54294  * 
54295  */
54296  
54297 Roo.DDView = function(container, tpl, config) {
54298     Roo.DDView.superclass.constructor.apply(this, arguments);
54299     this.getEl().setStyle("outline", "0px none");
54300     this.getEl().unselectable();
54301     if (this.dragGroup) {
54302         this.setDraggable(this.dragGroup.split(","));
54303     }
54304     if (this.dropGroup) {
54305         this.setDroppable(this.dropGroup.split(","));
54306     }
54307     if (this.deletable) {
54308         this.setDeletable();
54309     }
54310     this.isDirtyFlag = false;
54311         this.addEvents({
54312                 "drop" : true
54313         });
54314 };
54315
54316 Roo.extend(Roo.DDView, Roo.View, {
54317 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54318 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54319 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54320 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54321
54322         isFormField: true,
54323
54324         reset: Roo.emptyFn,
54325         
54326         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54327
54328         validate: function() {
54329                 return true;
54330         },
54331         
54332         destroy: function() {
54333                 this.purgeListeners();
54334                 this.getEl.removeAllListeners();
54335                 this.getEl().remove();
54336                 if (this.dragZone) {
54337                         if (this.dragZone.destroy) {
54338                                 this.dragZone.destroy();
54339                         }
54340                 }
54341                 if (this.dropZone) {
54342                         if (this.dropZone.destroy) {
54343                                 this.dropZone.destroy();
54344                         }
54345                 }
54346         },
54347
54348 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54349         getName: function() {
54350                 return this.name;
54351         },
54352
54353 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54354         setValue: function(v) {
54355                 if (!this.store) {
54356                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54357                 }
54358                 var data = {};
54359                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54360                 this.store.proxy = new Roo.data.MemoryProxy(data);
54361                 this.store.load();
54362         },
54363
54364 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54365         getValue: function() {
54366                 var result = '(';
54367                 this.store.each(function(rec) {
54368                         result += rec.id + ',';
54369                 });
54370                 return result.substr(0, result.length - 1) + ')';
54371         },
54372         
54373         getIds: function() {
54374                 var i = 0, result = new Array(this.store.getCount());
54375                 this.store.each(function(rec) {
54376                         result[i++] = rec.id;
54377                 });
54378                 return result;
54379         },
54380         
54381         isDirty: function() {
54382                 return this.isDirtyFlag;
54383         },
54384
54385 /**
54386  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54387  *      whole Element becomes the target, and this causes the drop gesture to append.
54388  */
54389     getTargetFromEvent : function(e) {
54390                 var target = e.getTarget();
54391                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54392                 target = target.parentNode;
54393                 }
54394                 if (!target) {
54395                         target = this.el.dom.lastChild || this.el.dom;
54396                 }
54397                 return target;
54398     },
54399
54400 /**
54401  *      Create the drag data which consists of an object which has the property "ddel" as
54402  *      the drag proxy element. 
54403  */
54404     getDragData : function(e) {
54405         var target = this.findItemFromChild(e.getTarget());
54406                 if(target) {
54407                         this.handleSelection(e);
54408                         var selNodes = this.getSelectedNodes();
54409             var dragData = {
54410                 source: this,
54411                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54412                 nodes: selNodes,
54413                 records: []
54414                         };
54415                         var selectedIndices = this.getSelectedIndexes();
54416                         for (var i = 0; i < selectedIndices.length; i++) {
54417                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54418                         }
54419                         if (selNodes.length == 1) {
54420                                 dragData.ddel = target.cloneNode(true); // the div element
54421                         } else {
54422                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54423                                 div.className = 'multi-proxy';
54424                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54425                                         div.appendChild(selNodes[i].cloneNode(true));
54426                                 }
54427                                 dragData.ddel = div;
54428                         }
54429             //console.log(dragData)
54430             //console.log(dragData.ddel.innerHTML)
54431                         return dragData;
54432                 }
54433         //console.log('nodragData')
54434                 return false;
54435     },
54436     
54437 /**     Specify to which ddGroup items in this DDView may be dragged. */
54438     setDraggable: function(ddGroup) {
54439         if (ddGroup instanceof Array) {
54440                 Roo.each(ddGroup, this.setDraggable, this);
54441                 return;
54442         }
54443         if (this.dragZone) {
54444                 this.dragZone.addToGroup(ddGroup);
54445         } else {
54446                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54447                                 containerScroll: true,
54448                                 ddGroup: ddGroup 
54449
54450                         });
54451 //                      Draggability implies selection. DragZone's mousedown selects the element.
54452                         if (!this.multiSelect) { this.singleSelect = true; }
54453
54454 //                      Wire the DragZone's handlers up to methods in *this*
54455                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54456                 }
54457     },
54458
54459 /**     Specify from which ddGroup this DDView accepts drops. */
54460     setDroppable: function(ddGroup) {
54461         if (ddGroup instanceof Array) {
54462                 Roo.each(ddGroup, this.setDroppable, this);
54463                 return;
54464         }
54465         if (this.dropZone) {
54466                 this.dropZone.addToGroup(ddGroup);
54467         } else {
54468                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54469                                 containerScroll: true,
54470                                 ddGroup: ddGroup
54471                         });
54472
54473 //                      Wire the DropZone's handlers up to methods in *this*
54474                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54475                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54476                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54477                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54478                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54479                 }
54480     },
54481
54482 /**     Decide whether to drop above or below a View node. */
54483     getDropPoint : function(e, n, dd){
54484         if (n == this.el.dom) { return "above"; }
54485                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54486                 var c = t + (b - t) / 2;
54487                 var y = Roo.lib.Event.getPageY(e);
54488                 if(y <= c) {
54489                         return "above";
54490                 }else{
54491                         return "below";
54492                 }
54493     },
54494
54495     onNodeEnter : function(n, dd, e, data){
54496                 return false;
54497     },
54498     
54499     onNodeOver : function(n, dd, e, data){
54500                 var pt = this.getDropPoint(e, n, dd);
54501                 // set the insert point style on the target node
54502                 var dragElClass = this.dropNotAllowed;
54503                 if (pt) {
54504                         var targetElClass;
54505                         if (pt == "above"){
54506                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54507                                 targetElClass = "x-view-drag-insert-above";
54508                         } else {
54509                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54510                                 targetElClass = "x-view-drag-insert-below";
54511                         }
54512                         if (this.lastInsertClass != targetElClass){
54513                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54514                                 this.lastInsertClass = targetElClass;
54515                         }
54516                 }
54517                 return dragElClass;
54518         },
54519
54520     onNodeOut : function(n, dd, e, data){
54521                 this.removeDropIndicators(n);
54522     },
54523
54524     onNodeDrop : function(n, dd, e, data){
54525         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54526                 return false;
54527         }
54528         var pt = this.getDropPoint(e, n, dd);
54529                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54530                 if (pt == "below") { insertAt++; }
54531                 for (var i = 0; i < data.records.length; i++) {
54532                         var r = data.records[i];
54533                         var dup = this.store.getById(r.id);
54534                         if (dup && (dd != this.dragZone)) {
54535                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54536                         } else {
54537                                 if (data.copy) {
54538                                         this.store.insert(insertAt++, r.copy());
54539                                 } else {
54540                                         data.source.isDirtyFlag = true;
54541                                         r.store.remove(r);
54542                                         this.store.insert(insertAt++, r);
54543                                 }
54544                                 this.isDirtyFlag = true;
54545                         }
54546                 }
54547                 this.dragZone.cachedTarget = null;
54548                 return true;
54549     },
54550
54551     removeDropIndicators : function(n){
54552                 if(n){
54553                         Roo.fly(n).removeClass([
54554                                 "x-view-drag-insert-above",
54555                                 "x-view-drag-insert-below"]);
54556                         this.lastInsertClass = "_noclass";
54557                 }
54558     },
54559
54560 /**
54561  *      Utility method. Add a delete option to the DDView's context menu.
54562  *      @param {String} imageUrl The URL of the "delete" icon image.
54563  */
54564         setDeletable: function(imageUrl) {
54565                 if (!this.singleSelect && !this.multiSelect) {
54566                         this.singleSelect = true;
54567                 }
54568                 var c = this.getContextMenu();
54569                 this.contextMenu.on("itemclick", function(item) {
54570                         switch (item.id) {
54571                                 case "delete":
54572                                         this.remove(this.getSelectedIndexes());
54573                                         break;
54574                         }
54575                 }, this);
54576                 this.contextMenu.add({
54577                         icon: imageUrl,
54578                         id: "delete",
54579                         text: 'Delete'
54580                 });
54581         },
54582         
54583 /**     Return the context menu for this DDView. */
54584         getContextMenu: function() {
54585                 if (!this.contextMenu) {
54586 //                      Create the View's context menu
54587                         this.contextMenu = new Roo.menu.Menu({
54588                                 id: this.id + "-contextmenu"
54589                         });
54590                         this.el.on("contextmenu", this.showContextMenu, this);
54591                 }
54592                 return this.contextMenu;
54593         },
54594         
54595         disableContextMenu: function() {
54596                 if (this.contextMenu) {
54597                         this.el.un("contextmenu", this.showContextMenu, this);
54598                 }
54599         },
54600
54601         showContextMenu: function(e, item) {
54602         item = this.findItemFromChild(e.getTarget());
54603                 if (item) {
54604                         e.stopEvent();
54605                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54606                         this.contextMenu.showAt(e.getXY());
54607             }
54608     },
54609
54610 /**
54611  *      Remove {@link Roo.data.Record}s at the specified indices.
54612  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54613  */
54614     remove: function(selectedIndices) {
54615                 selectedIndices = [].concat(selectedIndices);
54616                 for (var i = 0; i < selectedIndices.length; i++) {
54617                         var rec = this.store.getAt(selectedIndices[i]);
54618                         this.store.remove(rec);
54619                 }
54620     },
54621
54622 /**
54623  *      Double click fires the event, but also, if this is draggable, and there is only one other
54624  *      related DropZone, it transfers the selected node.
54625  */
54626     onDblClick : function(e){
54627         var item = this.findItemFromChild(e.getTarget());
54628         if(item){
54629             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54630                 return false;
54631             }
54632             if (this.dragGroup) {
54633                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54634                     while (targets.indexOf(this.dropZone) > -1) {
54635                             targets.remove(this.dropZone);
54636                                 }
54637                     if (targets.length == 1) {
54638                                         this.dragZone.cachedTarget = null;
54639                         var el = Roo.get(targets[0].getEl());
54640                         var box = el.getBox(true);
54641                         targets[0].onNodeDrop(el.dom, {
54642                                 target: el.dom,
54643                                 xy: [box.x, box.y + box.height - 1]
54644                         }, null, this.getDragData(e));
54645                     }
54646                 }
54647         }
54648     },
54649     
54650     handleSelection: function(e) {
54651                 this.dragZone.cachedTarget = null;
54652         var item = this.findItemFromChild(e.getTarget());
54653         if (!item) {
54654                 this.clearSelections(true);
54655                 return;
54656         }
54657                 if (item && (this.multiSelect || this.singleSelect)){
54658                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54659                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54660                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54661                                 this.unselect(item);
54662                         } else {
54663                                 this.select(item, this.multiSelect && e.ctrlKey);
54664                                 this.lastSelection = item;
54665                         }
54666                 }
54667     },
54668
54669     onItemClick : function(item, index, e){
54670                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54671                         return false;
54672                 }
54673                 return true;
54674     },
54675
54676     unselect : function(nodeInfo, suppressEvent){
54677                 var node = this.getNode(nodeInfo);
54678                 if(node && this.isSelected(node)){
54679                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54680                                 Roo.fly(node).removeClass(this.selectedClass);
54681                                 this.selections.remove(node);
54682                                 if(!suppressEvent){
54683                                         this.fireEvent("selectionchange", this, this.selections);
54684                                 }
54685                         }
54686                 }
54687     }
54688 });
54689 /*
54690  * Based on:
54691  * Ext JS Library 1.1.1
54692  * Copyright(c) 2006-2007, Ext JS, LLC.
54693  *
54694  * Originally Released Under LGPL - original licence link has changed is not relivant.
54695  *
54696  * Fork - LGPL
54697  * <script type="text/javascript">
54698  */
54699  
54700 /**
54701  * @class Roo.LayoutManager
54702  * @extends Roo.util.Observable
54703  * Base class for layout managers.
54704  */
54705 Roo.LayoutManager = function(container, config){
54706     Roo.LayoutManager.superclass.constructor.call(this);
54707     this.el = Roo.get(container);
54708     // ie scrollbar fix
54709     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54710         document.body.scroll = "no";
54711     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54712         this.el.position('relative');
54713     }
54714     this.id = this.el.id;
54715     this.el.addClass("x-layout-container");
54716     /** false to disable window resize monitoring @type Boolean */
54717     this.monitorWindowResize = true;
54718     this.regions = {};
54719     this.addEvents({
54720         /**
54721          * @event layout
54722          * Fires when a layout is performed. 
54723          * @param {Roo.LayoutManager} this
54724          */
54725         "layout" : true,
54726         /**
54727          * @event regionresized
54728          * Fires when the user resizes a region. 
54729          * @param {Roo.LayoutRegion} region The resized region
54730          * @param {Number} newSize The new size (width for east/west, height for north/south)
54731          */
54732         "regionresized" : true,
54733         /**
54734          * @event regioncollapsed
54735          * Fires when a region is collapsed. 
54736          * @param {Roo.LayoutRegion} region The collapsed region
54737          */
54738         "regioncollapsed" : true,
54739         /**
54740          * @event regionexpanded
54741          * Fires when a region is expanded.  
54742          * @param {Roo.LayoutRegion} region The expanded region
54743          */
54744         "regionexpanded" : true
54745     });
54746     this.updating = false;
54747     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54748 };
54749
54750 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54751     /**
54752      * Returns true if this layout is currently being updated
54753      * @return {Boolean}
54754      */
54755     isUpdating : function(){
54756         return this.updating; 
54757     },
54758     
54759     /**
54760      * Suspend the LayoutManager from doing auto-layouts while
54761      * making multiple add or remove calls
54762      */
54763     beginUpdate : function(){
54764         this.updating = true;    
54765     },
54766     
54767     /**
54768      * Restore auto-layouts and optionally disable the manager from performing a layout
54769      * @param {Boolean} noLayout true to disable a layout update 
54770      */
54771     endUpdate : function(noLayout){
54772         this.updating = false;
54773         if(!noLayout){
54774             this.layout();
54775         }    
54776     },
54777     
54778     layout: function(){
54779         
54780     },
54781     
54782     onRegionResized : function(region, newSize){
54783         this.fireEvent("regionresized", region, newSize);
54784         this.layout();
54785     },
54786     
54787     onRegionCollapsed : function(region){
54788         this.fireEvent("regioncollapsed", region);
54789     },
54790     
54791     onRegionExpanded : function(region){
54792         this.fireEvent("regionexpanded", region);
54793     },
54794         
54795     /**
54796      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54797      * performs box-model adjustments.
54798      * @return {Object} The size as an object {width: (the width), height: (the height)}
54799      */
54800     getViewSize : function(){
54801         var size;
54802         if(this.el.dom != document.body){
54803             size = this.el.getSize();
54804         }else{
54805             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54806         }
54807         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54808         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54809         return size;
54810     },
54811     
54812     /**
54813      * Returns the Element this layout is bound to.
54814      * @return {Roo.Element}
54815      */
54816     getEl : function(){
54817         return this.el;
54818     },
54819     
54820     /**
54821      * Returns the specified region.
54822      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54823      * @return {Roo.LayoutRegion}
54824      */
54825     getRegion : function(target){
54826         return this.regions[target.toLowerCase()];
54827     },
54828     
54829     onWindowResize : function(){
54830         if(this.monitorWindowResize){
54831             this.layout();
54832         }
54833     }
54834 });/*
54835  * Based on:
54836  * Ext JS Library 1.1.1
54837  * Copyright(c) 2006-2007, Ext JS, LLC.
54838  *
54839  * Originally Released Under LGPL - original licence link has changed is not relivant.
54840  *
54841  * Fork - LGPL
54842  * <script type="text/javascript">
54843  */
54844 /**
54845  * @class Roo.BorderLayout
54846  * @extends Roo.LayoutManager
54847  * @children Roo.ContentPanel
54848  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54849  * please see: <br><br>
54850  * <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>
54851  * <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>
54852  * Example:
54853  <pre><code>
54854  var layout = new Roo.BorderLayout(document.body, {
54855     north: {
54856         initialSize: 25,
54857         titlebar: false
54858     },
54859     west: {
54860         split:true,
54861         initialSize: 200,
54862         minSize: 175,
54863         maxSize: 400,
54864         titlebar: true,
54865         collapsible: true
54866     },
54867     east: {
54868         split:true,
54869         initialSize: 202,
54870         minSize: 175,
54871         maxSize: 400,
54872         titlebar: true,
54873         collapsible: true
54874     },
54875     south: {
54876         split:true,
54877         initialSize: 100,
54878         minSize: 100,
54879         maxSize: 200,
54880         titlebar: true,
54881         collapsible: true
54882     },
54883     center: {
54884         titlebar: true,
54885         autoScroll:true,
54886         resizeTabs: true,
54887         minTabWidth: 50,
54888         preferredTabWidth: 150
54889     }
54890 });
54891
54892 // shorthand
54893 var CP = Roo.ContentPanel;
54894
54895 layout.beginUpdate();
54896 layout.add("north", new CP("north", "North"));
54897 layout.add("south", new CP("south", {title: "South", closable: true}));
54898 layout.add("west", new CP("west", {title: "West"}));
54899 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54900 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54901 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54902 layout.getRegion("center").showPanel("center1");
54903 layout.endUpdate();
54904 </code></pre>
54905
54906 <b>The container the layout is rendered into can be either the body element or any other element.
54907 If it is not the body element, the container needs to either be an absolute positioned element,
54908 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54909 the container size if it is not the body element.</b>
54910
54911 * @constructor
54912 * Create a new BorderLayout
54913 * @param {String/HTMLElement/Element} container The container this layout is bound to
54914 * @param {Object} config Configuration options
54915  */
54916 Roo.BorderLayout = function(container, config){
54917     config = config || {};
54918     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54919     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54920     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54921         var target = this.factory.validRegions[i];
54922         if(config[target]){
54923             this.addRegion(target, config[target]);
54924         }
54925     }
54926 };
54927
54928 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54929         
54930         /**
54931          * @cfg {Roo.LayoutRegion} east
54932          */
54933         /**
54934          * @cfg {Roo.LayoutRegion} west
54935          */
54936         /**
54937          * @cfg {Roo.LayoutRegion} north
54938          */
54939         /**
54940          * @cfg {Roo.LayoutRegion} south
54941          */
54942         /**
54943          * @cfg {Roo.LayoutRegion} center
54944          */
54945     /**
54946      * Creates and adds a new region if it doesn't already exist.
54947      * @param {String} target The target region key (north, south, east, west or center).
54948      * @param {Object} config The regions config object
54949      * @return {BorderLayoutRegion} The new region
54950      */
54951     addRegion : function(target, config){
54952         if(!this.regions[target]){
54953             var r = this.factory.create(target, this, config);
54954             this.bindRegion(target, r);
54955         }
54956         return this.regions[target];
54957     },
54958
54959     // private (kinda)
54960     bindRegion : function(name, r){
54961         this.regions[name] = r;
54962         r.on("visibilitychange", this.layout, this);
54963         r.on("paneladded", this.layout, this);
54964         r.on("panelremoved", this.layout, this);
54965         r.on("invalidated", this.layout, this);
54966         r.on("resized", this.onRegionResized, this);
54967         r.on("collapsed", this.onRegionCollapsed, this);
54968         r.on("expanded", this.onRegionExpanded, this);
54969     },
54970
54971     /**
54972      * Performs a layout update.
54973      */
54974     layout : function(){
54975         if(this.updating) {
54976             return;
54977         }
54978         var size = this.getViewSize();
54979         var w = size.width;
54980         var h = size.height;
54981         var centerW = w;
54982         var centerH = h;
54983         var centerY = 0;
54984         var centerX = 0;
54985         //var x = 0, y = 0;
54986
54987         var rs = this.regions;
54988         var north = rs["north"];
54989         var south = rs["south"]; 
54990         var west = rs["west"];
54991         var east = rs["east"];
54992         var center = rs["center"];
54993         //if(this.hideOnLayout){ // not supported anymore
54994             //c.el.setStyle("display", "none");
54995         //}
54996         if(north && north.isVisible()){
54997             var b = north.getBox();
54998             var m = north.getMargins();
54999             b.width = w - (m.left+m.right);
55000             b.x = m.left;
55001             b.y = m.top;
55002             centerY = b.height + b.y + m.bottom;
55003             centerH -= centerY;
55004             north.updateBox(this.safeBox(b));
55005         }
55006         if(south && south.isVisible()){
55007             var b = south.getBox();
55008             var m = south.getMargins();
55009             b.width = w - (m.left+m.right);
55010             b.x = m.left;
55011             var totalHeight = (b.height + m.top + m.bottom);
55012             b.y = h - totalHeight + m.top;
55013             centerH -= totalHeight;
55014             south.updateBox(this.safeBox(b));
55015         }
55016         if(west && west.isVisible()){
55017             var b = west.getBox();
55018             var m = west.getMargins();
55019             b.height = centerH - (m.top+m.bottom);
55020             b.x = m.left;
55021             b.y = centerY + m.top;
55022             var totalWidth = (b.width + m.left + m.right);
55023             centerX += totalWidth;
55024             centerW -= totalWidth;
55025             west.updateBox(this.safeBox(b));
55026         }
55027         if(east && east.isVisible()){
55028             var b = east.getBox();
55029             var m = east.getMargins();
55030             b.height = centerH - (m.top+m.bottom);
55031             var totalWidth = (b.width + m.left + m.right);
55032             b.x = w - totalWidth + m.left;
55033             b.y = centerY + m.top;
55034             centerW -= totalWidth;
55035             east.updateBox(this.safeBox(b));
55036         }
55037         if(center){
55038             var m = center.getMargins();
55039             var centerBox = {
55040                 x: centerX + m.left,
55041                 y: centerY + m.top,
55042                 width: centerW - (m.left+m.right),
55043                 height: centerH - (m.top+m.bottom)
55044             };
55045             //if(this.hideOnLayout){
55046                 //center.el.setStyle("display", "block");
55047             //}
55048             center.updateBox(this.safeBox(centerBox));
55049         }
55050         this.el.repaint();
55051         this.fireEvent("layout", this);
55052     },
55053
55054     // private
55055     safeBox : function(box){
55056         box.width = Math.max(0, box.width);
55057         box.height = Math.max(0, box.height);
55058         return box;
55059     },
55060
55061     /**
55062      * Adds a ContentPanel (or subclass) to this layout.
55063      * @param {String} target The target region key (north, south, east, west or center).
55064      * @param {Roo.ContentPanel} panel The panel to add
55065      * @return {Roo.ContentPanel} The added panel
55066      */
55067     add : function(target, panel){
55068          
55069         target = target.toLowerCase();
55070         return this.regions[target].add(panel);
55071     },
55072
55073     /**
55074      * Remove a ContentPanel (or subclass) to this layout.
55075      * @param {String} target The target region key (north, south, east, west or center).
55076      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55077      * @return {Roo.ContentPanel} The removed panel
55078      */
55079     remove : function(target, panel){
55080         target = target.toLowerCase();
55081         return this.regions[target].remove(panel);
55082     },
55083
55084     /**
55085      * Searches all regions for a panel with the specified id
55086      * @param {String} panelId
55087      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55088      */
55089     findPanel : function(panelId){
55090         var rs = this.regions;
55091         for(var target in rs){
55092             if(typeof rs[target] != "function"){
55093                 var p = rs[target].getPanel(panelId);
55094                 if(p){
55095                     return p;
55096                 }
55097             }
55098         }
55099         return null;
55100     },
55101
55102     /**
55103      * Searches all regions for a panel with the specified id and activates (shows) it.
55104      * @param {String/ContentPanel} panelId The panels id or the panel itself
55105      * @return {Roo.ContentPanel} The shown panel or null
55106      */
55107     showPanel : function(panelId) {
55108       var rs = this.regions;
55109       for(var target in rs){
55110          var r = rs[target];
55111          if(typeof r != "function"){
55112             if(r.hasPanel(panelId)){
55113                return r.showPanel(panelId);
55114             }
55115          }
55116       }
55117       return null;
55118    },
55119
55120    /**
55121      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55122      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55123      */
55124     restoreState : function(provider){
55125         if(!provider){
55126             provider = Roo.state.Manager;
55127         }
55128         var sm = new Roo.LayoutStateManager();
55129         sm.init(this, provider);
55130     },
55131
55132     /**
55133      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55134      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55135      * a valid ContentPanel config object.  Example:
55136      * <pre><code>
55137 // Create the main layout
55138 var layout = new Roo.BorderLayout('main-ct', {
55139     west: {
55140         split:true,
55141         minSize: 175,
55142         titlebar: true
55143     },
55144     center: {
55145         title:'Components'
55146     }
55147 }, 'main-ct');
55148
55149 // Create and add multiple ContentPanels at once via configs
55150 layout.batchAdd({
55151    west: {
55152        id: 'source-files',
55153        autoCreate:true,
55154        title:'Ext Source Files',
55155        autoScroll:true,
55156        fitToFrame:true
55157    },
55158    center : {
55159        el: cview,
55160        autoScroll:true,
55161        fitToFrame:true,
55162        toolbar: tb,
55163        resizeEl:'cbody'
55164    }
55165 });
55166 </code></pre>
55167      * @param {Object} regions An object containing ContentPanel configs by region name
55168      */
55169     batchAdd : function(regions){
55170         this.beginUpdate();
55171         for(var rname in regions){
55172             var lr = this.regions[rname];
55173             if(lr){
55174                 this.addTypedPanels(lr, regions[rname]);
55175             }
55176         }
55177         this.endUpdate();
55178     },
55179
55180     // private
55181     addTypedPanels : function(lr, ps){
55182         if(typeof ps == 'string'){
55183             lr.add(new Roo.ContentPanel(ps));
55184         }
55185         else if(ps instanceof Array){
55186             for(var i =0, len = ps.length; i < len; i++){
55187                 this.addTypedPanels(lr, ps[i]);
55188             }
55189         }
55190         else if(!ps.events){ // raw config?
55191             var el = ps.el;
55192             delete ps.el; // prevent conflict
55193             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55194         }
55195         else {  // panel object assumed!
55196             lr.add(ps);
55197         }
55198     },
55199     /**
55200      * Adds a xtype elements to the layout.
55201      * <pre><code>
55202
55203 layout.addxtype({
55204        xtype : 'ContentPanel',
55205        region: 'west',
55206        items: [ .... ]
55207    }
55208 );
55209
55210 layout.addxtype({
55211         xtype : 'NestedLayoutPanel',
55212         region: 'west',
55213         layout: {
55214            center: { },
55215            west: { }   
55216         },
55217         items : [ ... list of content panels or nested layout panels.. ]
55218    }
55219 );
55220 </code></pre>
55221      * @param {Object} cfg Xtype definition of item to add.
55222      */
55223     addxtype : function(cfg)
55224     {
55225         // basically accepts a pannel...
55226         // can accept a layout region..!?!?
55227         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55228         
55229         if (!cfg.xtype.match(/Panel$/)) {
55230             return false;
55231         }
55232         var ret = false;
55233         
55234         if (typeof(cfg.region) == 'undefined') {
55235             Roo.log("Failed to add Panel, region was not set");
55236             Roo.log(cfg);
55237             return false;
55238         }
55239         var region = cfg.region;
55240         delete cfg.region;
55241         
55242           
55243         var xitems = [];
55244         if (cfg.items) {
55245             xitems = cfg.items;
55246             delete cfg.items;
55247         }
55248         var nb = false;
55249         
55250         switch(cfg.xtype) 
55251         {
55252             case 'ContentPanel':  // ContentPanel (el, cfg)
55253             case 'ScrollPanel':  // ContentPanel (el, cfg)
55254             case 'ViewPanel': 
55255                 if(cfg.autoCreate) {
55256                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55257                 } else {
55258                     var el = this.el.createChild();
55259                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55260                 }
55261                 
55262                 this.add(region, ret);
55263                 break;
55264             
55265             
55266             case 'TreePanel': // our new panel!
55267                 cfg.el = this.el.createChild();
55268                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55269                 this.add(region, ret);
55270                 break;
55271             
55272             case 'NestedLayoutPanel': 
55273                 // create a new Layout (which is  a Border Layout...
55274                 var el = this.el.createChild();
55275                 var clayout = cfg.layout;
55276                 delete cfg.layout;
55277                 clayout.items   = clayout.items  || [];
55278                 // replace this exitems with the clayout ones..
55279                 xitems = clayout.items;
55280                  
55281                 
55282                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55283                     cfg.background = false;
55284                 }
55285                 var layout = new Roo.BorderLayout(el, clayout);
55286                 
55287                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55288                 //console.log('adding nested layout panel '  + cfg.toSource());
55289                 this.add(region, ret);
55290                 nb = {}; /// find first...
55291                 break;
55292                 
55293             case 'GridPanel': 
55294             
55295                 // needs grid and region
55296                 
55297                 //var el = this.getRegion(region).el.createChild();
55298                 var el = this.el.createChild();
55299                 // create the grid first...
55300                 
55301                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55302                 delete cfg.grid;
55303                 if (region == 'center' && this.active ) {
55304                     cfg.background = false;
55305                 }
55306                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55307                 
55308                 this.add(region, ret);
55309                 if (cfg.background) {
55310                     ret.on('activate', function(gp) {
55311                         if (!gp.grid.rendered) {
55312                             gp.grid.render();
55313                         }
55314                     });
55315                 } else {
55316                     grid.render();
55317                 }
55318                 break;
55319            
55320            
55321            
55322                 
55323                 
55324                 
55325             default:
55326                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55327                     
55328                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55329                     this.add(region, ret);
55330                 } else {
55331                 
55332                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55333                     return null;
55334                 }
55335                 
55336              // GridPanel (grid, cfg)
55337             
55338         }
55339         this.beginUpdate();
55340         // add children..
55341         var region = '';
55342         var abn = {};
55343         Roo.each(xitems, function(i)  {
55344             region = nb && i.region ? i.region : false;
55345             
55346             var add = ret.addxtype(i);
55347            
55348             if (region) {
55349                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55350                 if (!i.background) {
55351                     abn[region] = nb[region] ;
55352                 }
55353             }
55354             
55355         });
55356         this.endUpdate();
55357
55358         // make the last non-background panel active..
55359         //if (nb) { Roo.log(abn); }
55360         if (nb) {
55361             
55362             for(var r in abn) {
55363                 region = this.getRegion(r);
55364                 if (region) {
55365                     // tried using nb[r], but it does not work..
55366                      
55367                     region.showPanel(abn[r]);
55368                    
55369                 }
55370             }
55371         }
55372         return ret;
55373         
55374     }
55375 });
55376
55377 /**
55378  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55379  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55380  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55381  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55382  * <pre><code>
55383 // shorthand
55384 var CP = Roo.ContentPanel;
55385
55386 var layout = Roo.BorderLayout.create({
55387     north: {
55388         initialSize: 25,
55389         titlebar: false,
55390         panels: [new CP("north", "North")]
55391     },
55392     west: {
55393         split:true,
55394         initialSize: 200,
55395         minSize: 175,
55396         maxSize: 400,
55397         titlebar: true,
55398         collapsible: true,
55399         panels: [new CP("west", {title: "West"})]
55400     },
55401     east: {
55402         split:true,
55403         initialSize: 202,
55404         minSize: 175,
55405         maxSize: 400,
55406         titlebar: true,
55407         collapsible: true,
55408         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55409     },
55410     south: {
55411         split:true,
55412         initialSize: 100,
55413         minSize: 100,
55414         maxSize: 200,
55415         titlebar: true,
55416         collapsible: true,
55417         panels: [new CP("south", {title: "South", closable: true})]
55418     },
55419     center: {
55420         titlebar: true,
55421         autoScroll:true,
55422         resizeTabs: true,
55423         minTabWidth: 50,
55424         preferredTabWidth: 150,
55425         panels: [
55426             new CP("center1", {title: "Close Me", closable: true}),
55427             new CP("center2", {title: "Center Panel", closable: false})
55428         ]
55429     }
55430 }, document.body);
55431
55432 layout.getRegion("center").showPanel("center1");
55433 </code></pre>
55434  * @param config
55435  * @param targetEl
55436  */
55437 Roo.BorderLayout.create = function(config, targetEl){
55438     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55439     layout.beginUpdate();
55440     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55441     for(var j = 0, jlen = regions.length; j < jlen; j++){
55442         var lr = regions[j];
55443         if(layout.regions[lr] && config[lr].panels){
55444             var r = layout.regions[lr];
55445             var ps = config[lr].panels;
55446             layout.addTypedPanels(r, ps);
55447         }
55448     }
55449     layout.endUpdate();
55450     return layout;
55451 };
55452
55453 // private
55454 Roo.BorderLayout.RegionFactory = {
55455     // private
55456     validRegions : ["north","south","east","west","center"],
55457
55458     // private
55459     create : function(target, mgr, config){
55460         target = target.toLowerCase();
55461         if(config.lightweight || config.basic){
55462             return new Roo.BasicLayoutRegion(mgr, config, target);
55463         }
55464         switch(target){
55465             case "north":
55466                 return new Roo.NorthLayoutRegion(mgr, config);
55467             case "south":
55468                 return new Roo.SouthLayoutRegion(mgr, config);
55469             case "east":
55470                 return new Roo.EastLayoutRegion(mgr, config);
55471             case "west":
55472                 return new Roo.WestLayoutRegion(mgr, config);
55473             case "center":
55474                 return new Roo.CenterLayoutRegion(mgr, config);
55475         }
55476         throw 'Layout region "'+target+'" not supported.';
55477     }
55478 };/*
55479  * Based on:
55480  * Ext JS Library 1.1.1
55481  * Copyright(c) 2006-2007, Ext JS, LLC.
55482  *
55483  * Originally Released Under LGPL - original licence link has changed is not relivant.
55484  *
55485  * Fork - LGPL
55486  * <script type="text/javascript">
55487  */
55488  
55489 /**
55490  * @class Roo.BasicLayoutRegion
55491  * @extends Roo.util.Observable
55492  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55493  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55494  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55495  */
55496 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55497     this.mgr = mgr;
55498     this.position  = pos;
55499     this.events = {
55500         /**
55501          * @scope Roo.BasicLayoutRegion
55502          */
55503         
55504         /**
55505          * @event beforeremove
55506          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55507          * @param {Roo.LayoutRegion} this
55508          * @param {Roo.ContentPanel} panel The panel
55509          * @param {Object} e The cancel event object
55510          */
55511         "beforeremove" : true,
55512         /**
55513          * @event invalidated
55514          * Fires when the layout for this region is changed.
55515          * @param {Roo.LayoutRegion} this
55516          */
55517         "invalidated" : true,
55518         /**
55519          * @event visibilitychange
55520          * Fires when this region is shown or hidden 
55521          * @param {Roo.LayoutRegion} this
55522          * @param {Boolean} visibility true or false
55523          */
55524         "visibilitychange" : true,
55525         /**
55526          * @event paneladded
55527          * Fires when a panel is added. 
55528          * @param {Roo.LayoutRegion} this
55529          * @param {Roo.ContentPanel} panel The panel
55530          */
55531         "paneladded" : true,
55532         /**
55533          * @event panelremoved
55534          * Fires when a panel is removed. 
55535          * @param {Roo.LayoutRegion} this
55536          * @param {Roo.ContentPanel} panel The panel
55537          */
55538         "panelremoved" : true,
55539         /**
55540          * @event beforecollapse
55541          * Fires when this region before collapse.
55542          * @param {Roo.LayoutRegion} this
55543          */
55544         "beforecollapse" : true,
55545         /**
55546          * @event collapsed
55547          * Fires when this region is collapsed.
55548          * @param {Roo.LayoutRegion} this
55549          */
55550         "collapsed" : true,
55551         /**
55552          * @event expanded
55553          * Fires when this region is expanded.
55554          * @param {Roo.LayoutRegion} this
55555          */
55556         "expanded" : true,
55557         /**
55558          * @event slideshow
55559          * Fires when this region is slid into view.
55560          * @param {Roo.LayoutRegion} this
55561          */
55562         "slideshow" : true,
55563         /**
55564          * @event slidehide
55565          * Fires when this region slides out of view. 
55566          * @param {Roo.LayoutRegion} this
55567          */
55568         "slidehide" : true,
55569         /**
55570          * @event panelactivated
55571          * Fires when a panel is activated. 
55572          * @param {Roo.LayoutRegion} this
55573          * @param {Roo.ContentPanel} panel The activated panel
55574          */
55575         "panelactivated" : true,
55576         /**
55577          * @event resized
55578          * Fires when the user resizes this region. 
55579          * @param {Roo.LayoutRegion} this
55580          * @param {Number} newSize The new size (width for east/west, height for north/south)
55581          */
55582         "resized" : true
55583     };
55584     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55585     this.panels = new Roo.util.MixedCollection();
55586     this.panels.getKey = this.getPanelId.createDelegate(this);
55587     this.box = null;
55588     this.activePanel = null;
55589     // ensure listeners are added...
55590     
55591     if (config.listeners || config.events) {
55592         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55593             listeners : config.listeners || {},
55594             events : config.events || {}
55595         });
55596     }
55597     
55598     if(skipConfig !== true){
55599         this.applyConfig(config);
55600     }
55601 };
55602
55603 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55604     getPanelId : function(p){
55605         return p.getId();
55606     },
55607     
55608     applyConfig : function(config){
55609         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55610         this.config = config;
55611         
55612     },
55613     
55614     /**
55615      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55616      * the width, for horizontal (north, south) the height.
55617      * @param {Number} newSize The new width or height
55618      */
55619     resizeTo : function(newSize){
55620         var el = this.el ? this.el :
55621                  (this.activePanel ? this.activePanel.getEl() : null);
55622         if(el){
55623             switch(this.position){
55624                 case "east":
55625                 case "west":
55626                     el.setWidth(newSize);
55627                     this.fireEvent("resized", this, newSize);
55628                 break;
55629                 case "north":
55630                 case "south":
55631                     el.setHeight(newSize);
55632                     this.fireEvent("resized", this, newSize);
55633                 break;                
55634             }
55635         }
55636     },
55637     
55638     getBox : function(){
55639         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55640     },
55641     
55642     getMargins : function(){
55643         return this.margins;
55644     },
55645     
55646     updateBox : function(box){
55647         this.box = box;
55648         var el = this.activePanel.getEl();
55649         el.dom.style.left = box.x + "px";
55650         el.dom.style.top = box.y + "px";
55651         this.activePanel.setSize(box.width, box.height);
55652     },
55653     
55654     /**
55655      * Returns the container element for this region.
55656      * @return {Roo.Element}
55657      */
55658     getEl : function(){
55659         return this.activePanel;
55660     },
55661     
55662     /**
55663      * Returns true if this region is currently visible.
55664      * @return {Boolean}
55665      */
55666     isVisible : function(){
55667         return this.activePanel ? true : false;
55668     },
55669     
55670     setActivePanel : function(panel){
55671         panel = this.getPanel(panel);
55672         if(this.activePanel && this.activePanel != panel){
55673             this.activePanel.setActiveState(false);
55674             this.activePanel.getEl().setLeftTop(-10000,-10000);
55675         }
55676         this.activePanel = panel;
55677         panel.setActiveState(true);
55678         if(this.box){
55679             panel.setSize(this.box.width, this.box.height);
55680         }
55681         this.fireEvent("panelactivated", this, panel);
55682         this.fireEvent("invalidated");
55683     },
55684     
55685     /**
55686      * Show the specified panel.
55687      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55688      * @return {Roo.ContentPanel} The shown panel or null
55689      */
55690     showPanel : function(panel){
55691         if(panel = this.getPanel(panel)){
55692             this.setActivePanel(panel);
55693         }
55694         return panel;
55695     },
55696     
55697     /**
55698      * Get the active panel for this region.
55699      * @return {Roo.ContentPanel} The active panel or null
55700      */
55701     getActivePanel : function(){
55702         return this.activePanel;
55703     },
55704     
55705     /**
55706      * Add the passed ContentPanel(s)
55707      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55708      * @return {Roo.ContentPanel} The panel added (if only one was added)
55709      */
55710     add : function(panel){
55711         if(arguments.length > 1){
55712             for(var i = 0, len = arguments.length; i < len; i++) {
55713                 this.add(arguments[i]);
55714             }
55715             return null;
55716         }
55717         if(this.hasPanel(panel)){
55718             this.showPanel(panel);
55719             return panel;
55720         }
55721         var el = panel.getEl();
55722         if(el.dom.parentNode != this.mgr.el.dom){
55723             this.mgr.el.dom.appendChild(el.dom);
55724         }
55725         if(panel.setRegion){
55726             panel.setRegion(this);
55727         }
55728         this.panels.add(panel);
55729         el.setStyle("position", "absolute");
55730         if(!panel.background){
55731             this.setActivePanel(panel);
55732             if(this.config.initialSize && this.panels.getCount()==1){
55733                 this.resizeTo(this.config.initialSize);
55734             }
55735         }
55736         this.fireEvent("paneladded", this, panel);
55737         return panel;
55738     },
55739     
55740     /**
55741      * Returns true if the panel is in this region.
55742      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55743      * @return {Boolean}
55744      */
55745     hasPanel : function(panel){
55746         if(typeof panel == "object"){ // must be panel obj
55747             panel = panel.getId();
55748         }
55749         return this.getPanel(panel) ? true : false;
55750     },
55751     
55752     /**
55753      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55754      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55755      * @param {Boolean} preservePanel Overrides the config preservePanel option
55756      * @return {Roo.ContentPanel} The panel that was removed
55757      */
55758     remove : function(panel, preservePanel){
55759         panel = this.getPanel(panel);
55760         if(!panel){
55761             return null;
55762         }
55763         var e = {};
55764         this.fireEvent("beforeremove", this, panel, e);
55765         if(e.cancel === true){
55766             return null;
55767         }
55768         var panelId = panel.getId();
55769         this.panels.removeKey(panelId);
55770         return panel;
55771     },
55772     
55773     /**
55774      * Returns the panel specified or null if it's not in this region.
55775      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55776      * @return {Roo.ContentPanel}
55777      */
55778     getPanel : function(id){
55779         if(typeof id == "object"){ // must be panel obj
55780             return id;
55781         }
55782         return this.panels.get(id);
55783     },
55784     
55785     /**
55786      * Returns this regions position (north/south/east/west/center).
55787      * @return {String} 
55788      */
55789     getPosition: function(){
55790         return this.position;    
55791     }
55792 });/*
55793  * Based on:
55794  * Ext JS Library 1.1.1
55795  * Copyright(c) 2006-2007, Ext JS, LLC.
55796  *
55797  * Originally Released Under LGPL - original licence link has changed is not relivant.
55798  *
55799  * Fork - LGPL
55800  * <script type="text/javascript">
55801  */
55802  
55803 /**
55804  * @class Roo.LayoutRegion
55805  * @extends Roo.BasicLayoutRegion
55806  * This class represents a region in a layout manager.
55807  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55808  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55809  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55810  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55811  * @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})
55812  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55813  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55814  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55815  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55816  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55817  * @cfg {String}    title           The title for the region (overrides panel titles)
55818  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55819  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55820  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55821  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55822  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55823  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55824  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55825  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55826  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55827  * @cfg {Boolean}   showPin         True to show a pin button
55828  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55829  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55830  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55831  * @cfg {Number}    width           For East/West panels
55832  * @cfg {Number}    height          For North/South panels
55833  * @cfg {Boolean}   split           To show the splitter
55834  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55835  */
55836 Roo.LayoutRegion = function(mgr, config, pos){
55837     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55838     var dh = Roo.DomHelper;
55839     /** This region's container element 
55840     * @type Roo.Element */
55841     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55842     /** This region's title element 
55843     * @type Roo.Element */
55844
55845     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55846         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55847         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55848     ]}, true);
55849     this.titleEl.enableDisplayMode();
55850     /** This region's title text element 
55851     * @type HTMLElement */
55852     this.titleTextEl = this.titleEl.dom.firstChild;
55853     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55854     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55855     this.closeBtn.enableDisplayMode();
55856     this.closeBtn.on("click", this.closeClicked, this);
55857     this.closeBtn.hide();
55858
55859     this.createBody(config);
55860     this.visible = true;
55861     this.collapsed = false;
55862
55863     if(config.hideWhenEmpty){
55864         this.hide();
55865         this.on("paneladded", this.validateVisibility, this);
55866         this.on("panelremoved", this.validateVisibility, this);
55867     }
55868     this.applyConfig(config);
55869 };
55870
55871 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55872
55873     createBody : function(){
55874         /** This region's body element 
55875         * @type Roo.Element */
55876         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55877     },
55878
55879     applyConfig : function(c){
55880         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55881             var dh = Roo.DomHelper;
55882             if(c.titlebar !== false){
55883                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55884                 this.collapseBtn.on("click", this.collapse, this);
55885                 this.collapseBtn.enableDisplayMode();
55886
55887                 if(c.showPin === true || this.showPin){
55888                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55889                     this.stickBtn.enableDisplayMode();
55890                     this.stickBtn.on("click", this.expand, this);
55891                     this.stickBtn.hide();
55892                 }
55893             }
55894             /** This region's collapsed element
55895             * @type Roo.Element */
55896             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55897                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55898             ]}, true);
55899             if(c.floatable !== false){
55900                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55901                this.collapsedEl.on("click", this.collapseClick, this);
55902             }
55903
55904             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55905                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55906                    id: "message", unselectable: "on", style:{"float":"left"}});
55907                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55908              }
55909             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55910             this.expandBtn.on("click", this.expand, this);
55911         }
55912         if(this.collapseBtn){
55913             this.collapseBtn.setVisible(c.collapsible == true);
55914         }
55915         this.cmargins = c.cmargins || this.cmargins ||
55916                          (this.position == "west" || this.position == "east" ?
55917                              {top: 0, left: 2, right:2, bottom: 0} :
55918                              {top: 2, left: 0, right:0, bottom: 2});
55919         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55920         this.bottomTabs = c.tabPosition != "top";
55921         this.autoScroll = c.autoScroll || false;
55922         if(this.autoScroll){
55923             this.bodyEl.setStyle("overflow", "auto");
55924         }else{
55925             this.bodyEl.setStyle("overflow", "hidden");
55926         }
55927         //if(c.titlebar !== false){
55928             if((!c.titlebar && !c.title) || c.titlebar === false){
55929                 this.titleEl.hide();
55930             }else{
55931                 this.titleEl.show();
55932                 if(c.title){
55933                     this.titleTextEl.innerHTML = c.title;
55934                 }
55935             }
55936         //}
55937         this.duration = c.duration || .30;
55938         this.slideDuration = c.slideDuration || .45;
55939         this.config = c;
55940         if(c.collapsed){
55941             this.collapse(true);
55942         }
55943         if(c.hidden){
55944             this.hide();
55945         }
55946     },
55947     /**
55948      * Returns true if this region is currently visible.
55949      * @return {Boolean}
55950      */
55951     isVisible : function(){
55952         return this.visible;
55953     },
55954
55955     /**
55956      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55957      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55958      */
55959     setCollapsedTitle : function(title){
55960         title = title || "&#160;";
55961         if(this.collapsedTitleTextEl){
55962             this.collapsedTitleTextEl.innerHTML = title;
55963         }
55964     },
55965
55966     getBox : function(){
55967         var b;
55968         if(!this.collapsed){
55969             b = this.el.getBox(false, true);
55970         }else{
55971             b = this.collapsedEl.getBox(false, true);
55972         }
55973         return b;
55974     },
55975
55976     getMargins : function(){
55977         return this.collapsed ? this.cmargins : this.margins;
55978     },
55979
55980     highlight : function(){
55981         this.el.addClass("x-layout-panel-dragover");
55982     },
55983
55984     unhighlight : function(){
55985         this.el.removeClass("x-layout-panel-dragover");
55986     },
55987
55988     updateBox : function(box){
55989         this.box = box;
55990         if(!this.collapsed){
55991             this.el.dom.style.left = box.x + "px";
55992             this.el.dom.style.top = box.y + "px";
55993             this.updateBody(box.width, box.height);
55994         }else{
55995             this.collapsedEl.dom.style.left = box.x + "px";
55996             this.collapsedEl.dom.style.top = box.y + "px";
55997             this.collapsedEl.setSize(box.width, box.height);
55998         }
55999         if(this.tabs){
56000             this.tabs.autoSizeTabs();
56001         }
56002     },
56003
56004     updateBody : function(w, h){
56005         if(w !== null){
56006             this.el.setWidth(w);
56007             w -= this.el.getBorderWidth("rl");
56008             if(this.config.adjustments){
56009                 w += this.config.adjustments[0];
56010             }
56011         }
56012         if(h !== null){
56013             this.el.setHeight(h);
56014             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56015             h -= this.el.getBorderWidth("tb");
56016             if(this.config.adjustments){
56017                 h += this.config.adjustments[1];
56018             }
56019             this.bodyEl.setHeight(h);
56020             if(this.tabs){
56021                 h = this.tabs.syncHeight(h);
56022             }
56023         }
56024         if(this.panelSize){
56025             w = w !== null ? w : this.panelSize.width;
56026             h = h !== null ? h : this.panelSize.height;
56027         }
56028         if(this.activePanel){
56029             var el = this.activePanel.getEl();
56030             w = w !== null ? w : el.getWidth();
56031             h = h !== null ? h : el.getHeight();
56032             this.panelSize = {width: w, height: h};
56033             this.activePanel.setSize(w, h);
56034         }
56035         if(Roo.isIE && this.tabs){
56036             this.tabs.el.repaint();
56037         }
56038     },
56039
56040     /**
56041      * Returns the container element for this region.
56042      * @return {Roo.Element}
56043      */
56044     getEl : function(){
56045         return this.el;
56046     },
56047
56048     /**
56049      * Hides this region.
56050      */
56051     hide : function(){
56052         if(!this.collapsed){
56053             this.el.dom.style.left = "-2000px";
56054             this.el.hide();
56055         }else{
56056             this.collapsedEl.dom.style.left = "-2000px";
56057             this.collapsedEl.hide();
56058         }
56059         this.visible = false;
56060         this.fireEvent("visibilitychange", this, false);
56061     },
56062
56063     /**
56064      * Shows this region if it was previously hidden.
56065      */
56066     show : function(){
56067         if(!this.collapsed){
56068             this.el.show();
56069         }else{
56070             this.collapsedEl.show();
56071         }
56072         this.visible = true;
56073         this.fireEvent("visibilitychange", this, true);
56074     },
56075
56076     closeClicked : function(){
56077         if(this.activePanel){
56078             this.remove(this.activePanel);
56079         }
56080     },
56081
56082     collapseClick : function(e){
56083         if(this.isSlid){
56084            e.stopPropagation();
56085            this.slideIn();
56086         }else{
56087            e.stopPropagation();
56088            this.slideOut();
56089         }
56090     },
56091
56092     /**
56093      * Collapses this region.
56094      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56095      */
56096     collapse : function(skipAnim, skipCheck){
56097         if(this.collapsed) {
56098             return;
56099         }
56100         
56101         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56102             
56103             this.collapsed = true;
56104             if(this.split){
56105                 this.split.el.hide();
56106             }
56107             if(this.config.animate && skipAnim !== true){
56108                 this.fireEvent("invalidated", this);
56109                 this.animateCollapse();
56110             }else{
56111                 this.el.setLocation(-20000,-20000);
56112                 this.el.hide();
56113                 this.collapsedEl.show();
56114                 this.fireEvent("collapsed", this);
56115                 this.fireEvent("invalidated", this);
56116             }
56117         }
56118         
56119     },
56120
56121     animateCollapse : function(){
56122         // overridden
56123     },
56124
56125     /**
56126      * Expands this region if it was previously collapsed.
56127      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56128      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56129      */
56130     expand : function(e, skipAnim){
56131         if(e) {
56132             e.stopPropagation();
56133         }
56134         if(!this.collapsed || this.el.hasActiveFx()) {
56135             return;
56136         }
56137         if(this.isSlid){
56138             this.afterSlideIn();
56139             skipAnim = true;
56140         }
56141         this.collapsed = false;
56142         if(this.config.animate && skipAnim !== true){
56143             this.animateExpand();
56144         }else{
56145             this.el.show();
56146             if(this.split){
56147                 this.split.el.show();
56148             }
56149             this.collapsedEl.setLocation(-2000,-2000);
56150             this.collapsedEl.hide();
56151             this.fireEvent("invalidated", this);
56152             this.fireEvent("expanded", this);
56153         }
56154     },
56155
56156     animateExpand : function(){
56157         // overridden
56158     },
56159
56160     initTabs : function()
56161     {
56162         this.bodyEl.setStyle("overflow", "hidden");
56163         var ts = new Roo.TabPanel(
56164                 this.bodyEl.dom,
56165                 {
56166                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56167                     disableTooltips: this.config.disableTabTips,
56168                     toolbar : this.config.toolbar
56169                 }
56170         );
56171         if(this.config.hideTabs){
56172             ts.stripWrap.setDisplayed(false);
56173         }
56174         this.tabs = ts;
56175         ts.resizeTabs = this.config.resizeTabs === true;
56176         ts.minTabWidth = this.config.minTabWidth || 40;
56177         ts.maxTabWidth = this.config.maxTabWidth || 250;
56178         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56179         ts.monitorResize = false;
56180         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56181         ts.bodyEl.addClass('x-layout-tabs-body');
56182         this.panels.each(this.initPanelAsTab, this);
56183     },
56184
56185     initPanelAsTab : function(panel){
56186         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56187                     this.config.closeOnTab && panel.isClosable());
56188         if(panel.tabTip !== undefined){
56189             ti.setTooltip(panel.tabTip);
56190         }
56191         ti.on("activate", function(){
56192               this.setActivePanel(panel);
56193         }, this);
56194         if(this.config.closeOnTab){
56195             ti.on("beforeclose", function(t, e){
56196                 e.cancel = true;
56197                 this.remove(panel);
56198             }, this);
56199         }
56200         return ti;
56201     },
56202
56203     updatePanelTitle : function(panel, title){
56204         if(this.activePanel == panel){
56205             this.updateTitle(title);
56206         }
56207         if(this.tabs){
56208             var ti = this.tabs.getTab(panel.getEl().id);
56209             ti.setText(title);
56210             if(panel.tabTip !== undefined){
56211                 ti.setTooltip(panel.tabTip);
56212             }
56213         }
56214     },
56215
56216     updateTitle : function(title){
56217         if(this.titleTextEl && !this.config.title){
56218             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56219         }
56220     },
56221
56222     setActivePanel : function(panel){
56223         panel = this.getPanel(panel);
56224         if(this.activePanel && this.activePanel != panel){
56225             this.activePanel.setActiveState(false);
56226         }
56227         this.activePanel = panel;
56228         panel.setActiveState(true);
56229         if(this.panelSize){
56230             panel.setSize(this.panelSize.width, this.panelSize.height);
56231         }
56232         if(this.closeBtn){
56233             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56234         }
56235         this.updateTitle(panel.getTitle());
56236         if(this.tabs){
56237             this.fireEvent("invalidated", this);
56238         }
56239         this.fireEvent("panelactivated", this, panel);
56240     },
56241
56242     /**
56243      * Shows the specified panel.
56244      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56245      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56246      */
56247     showPanel : function(panel)
56248     {
56249         panel = this.getPanel(panel);
56250         if(panel){
56251             if(this.tabs){
56252                 var tab = this.tabs.getTab(panel.getEl().id);
56253                 if(tab.isHidden()){
56254                     this.tabs.unhideTab(tab.id);
56255                 }
56256                 tab.activate();
56257             }else{
56258                 this.setActivePanel(panel);
56259             }
56260         }
56261         return panel;
56262     },
56263
56264     /**
56265      * Get the active panel for this region.
56266      * @return {Roo.ContentPanel} The active panel or null
56267      */
56268     getActivePanel : function(){
56269         return this.activePanel;
56270     },
56271
56272     validateVisibility : function(){
56273         if(this.panels.getCount() < 1){
56274             this.updateTitle("&#160;");
56275             this.closeBtn.hide();
56276             this.hide();
56277         }else{
56278             if(!this.isVisible()){
56279                 this.show();
56280             }
56281         }
56282     },
56283
56284     /**
56285      * Adds the passed ContentPanel(s) to this region.
56286      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56287      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56288      */
56289     add : function(panel){
56290         if(arguments.length > 1){
56291             for(var i = 0, len = arguments.length; i < len; i++) {
56292                 this.add(arguments[i]);
56293             }
56294             return null;
56295         }
56296         if(this.hasPanel(panel)){
56297             this.showPanel(panel);
56298             return panel;
56299         }
56300         panel.setRegion(this);
56301         this.panels.add(panel);
56302         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56303             this.bodyEl.dom.appendChild(panel.getEl().dom);
56304             if(panel.background !== true){
56305                 this.setActivePanel(panel);
56306             }
56307             this.fireEvent("paneladded", this, panel);
56308             return panel;
56309         }
56310         if(!this.tabs){
56311             this.initTabs();
56312         }else{
56313             this.initPanelAsTab(panel);
56314         }
56315         if(panel.background !== true){
56316             this.tabs.activate(panel.getEl().id);
56317         }
56318         this.fireEvent("paneladded", this, panel);
56319         return panel;
56320     },
56321
56322     /**
56323      * Hides the tab for the specified panel.
56324      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56325      */
56326     hidePanel : function(panel){
56327         if(this.tabs && (panel = this.getPanel(panel))){
56328             this.tabs.hideTab(panel.getEl().id);
56329         }
56330     },
56331
56332     /**
56333      * Unhides the tab for a previously hidden panel.
56334      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56335      */
56336     unhidePanel : function(panel){
56337         if(this.tabs && (panel = this.getPanel(panel))){
56338             this.tabs.unhideTab(panel.getEl().id);
56339         }
56340     },
56341
56342     clearPanels : function(){
56343         while(this.panels.getCount() > 0){
56344              this.remove(this.panels.first());
56345         }
56346     },
56347
56348     /**
56349      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56351      * @param {Boolean} preservePanel Overrides the config preservePanel option
56352      * @return {Roo.ContentPanel} The panel that was removed
56353      */
56354     remove : function(panel, preservePanel){
56355         panel = this.getPanel(panel);
56356         if(!panel){
56357             return null;
56358         }
56359         var e = {};
56360         this.fireEvent("beforeremove", this, panel, e);
56361         if(e.cancel === true){
56362             return null;
56363         }
56364         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56365         var panelId = panel.getId();
56366         this.panels.removeKey(panelId);
56367         if(preservePanel){
56368             document.body.appendChild(panel.getEl().dom);
56369         }
56370         if(this.tabs){
56371             this.tabs.removeTab(panel.getEl().id);
56372         }else if (!preservePanel){
56373             this.bodyEl.dom.removeChild(panel.getEl().dom);
56374         }
56375         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56376             var p = this.panels.first();
56377             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56378             tempEl.appendChild(p.getEl().dom);
56379             this.bodyEl.update("");
56380             this.bodyEl.dom.appendChild(p.getEl().dom);
56381             tempEl = null;
56382             this.updateTitle(p.getTitle());
56383             this.tabs = null;
56384             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56385             this.setActivePanel(p);
56386         }
56387         panel.setRegion(null);
56388         if(this.activePanel == panel){
56389             this.activePanel = null;
56390         }
56391         if(this.config.autoDestroy !== false && preservePanel !== true){
56392             try{panel.destroy();}catch(e){}
56393         }
56394         this.fireEvent("panelremoved", this, panel);
56395         return panel;
56396     },
56397
56398     /**
56399      * Returns the TabPanel component used by this region
56400      * @return {Roo.TabPanel}
56401      */
56402     getTabs : function(){
56403         return this.tabs;
56404     },
56405
56406     createTool : function(parentEl, className){
56407         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56408             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56409         btn.addClassOnOver("x-layout-tools-button-over");
56410         return btn;
56411     }
56412 });/*
56413  * Based on:
56414  * Ext JS Library 1.1.1
56415  * Copyright(c) 2006-2007, Ext JS, LLC.
56416  *
56417  * Originally Released Under LGPL - original licence link has changed is not relivant.
56418  *
56419  * Fork - LGPL
56420  * <script type="text/javascript">
56421  */
56422  
56423
56424
56425 /**
56426  * @class Roo.SplitLayoutRegion
56427  * @extends Roo.LayoutRegion
56428  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56429  */
56430 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56431     this.cursor = cursor;
56432     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56433 };
56434
56435 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56436     splitTip : "Drag to resize.",
56437     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56438     useSplitTips : false,
56439
56440     applyConfig : function(config){
56441         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56442         if(config.split){
56443             if(!this.split){
56444                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56445                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56446                 /** The SplitBar for this region 
56447                 * @type Roo.SplitBar */
56448                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56449                 this.split.on("moved", this.onSplitMove, this);
56450                 this.split.useShim = config.useShim === true;
56451                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56452                 if(this.useSplitTips){
56453                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56454                 }
56455                 if(config.collapsible){
56456                     this.split.el.on("dblclick", this.collapse,  this);
56457                 }
56458             }
56459             if(typeof config.minSize != "undefined"){
56460                 this.split.minSize = config.minSize;
56461             }
56462             if(typeof config.maxSize != "undefined"){
56463                 this.split.maxSize = config.maxSize;
56464             }
56465             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56466                 this.hideSplitter();
56467             }
56468         }
56469     },
56470
56471     getHMaxSize : function(){
56472          var cmax = this.config.maxSize || 10000;
56473          var center = this.mgr.getRegion("center");
56474          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56475     },
56476
56477     getVMaxSize : function(){
56478          var cmax = this.config.maxSize || 10000;
56479          var center = this.mgr.getRegion("center");
56480          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56481     },
56482
56483     onSplitMove : function(split, newSize){
56484         this.fireEvent("resized", this, newSize);
56485     },
56486     
56487     /** 
56488      * Returns the {@link Roo.SplitBar} for this region.
56489      * @return {Roo.SplitBar}
56490      */
56491     getSplitBar : function(){
56492         return this.split;
56493     },
56494     
56495     hide : function(){
56496         this.hideSplitter();
56497         Roo.SplitLayoutRegion.superclass.hide.call(this);
56498     },
56499
56500     hideSplitter : function(){
56501         if(this.split){
56502             this.split.el.setLocation(-2000,-2000);
56503             this.split.el.hide();
56504         }
56505     },
56506
56507     show : function(){
56508         if(this.split){
56509             this.split.el.show();
56510         }
56511         Roo.SplitLayoutRegion.superclass.show.call(this);
56512     },
56513     
56514     beforeSlide: function(){
56515         if(Roo.isGecko){// firefox overflow auto bug workaround
56516             this.bodyEl.clip();
56517             if(this.tabs) {
56518                 this.tabs.bodyEl.clip();
56519             }
56520             if(this.activePanel){
56521                 this.activePanel.getEl().clip();
56522                 
56523                 if(this.activePanel.beforeSlide){
56524                     this.activePanel.beforeSlide();
56525                 }
56526             }
56527         }
56528     },
56529     
56530     afterSlide : function(){
56531         if(Roo.isGecko){// firefox overflow auto bug workaround
56532             this.bodyEl.unclip();
56533             if(this.tabs) {
56534                 this.tabs.bodyEl.unclip();
56535             }
56536             if(this.activePanel){
56537                 this.activePanel.getEl().unclip();
56538                 if(this.activePanel.afterSlide){
56539                     this.activePanel.afterSlide();
56540                 }
56541             }
56542         }
56543     },
56544
56545     initAutoHide : function(){
56546         if(this.autoHide !== false){
56547             if(!this.autoHideHd){
56548                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56549                 this.autoHideHd = {
56550                     "mouseout": function(e){
56551                         if(!e.within(this.el, true)){
56552                             st.delay(500);
56553                         }
56554                     },
56555                     "mouseover" : function(e){
56556                         st.cancel();
56557                     },
56558                     scope : this
56559                 };
56560             }
56561             this.el.on(this.autoHideHd);
56562         }
56563     },
56564
56565     clearAutoHide : function(){
56566         if(this.autoHide !== false){
56567             this.el.un("mouseout", this.autoHideHd.mouseout);
56568             this.el.un("mouseover", this.autoHideHd.mouseover);
56569         }
56570     },
56571
56572     clearMonitor : function(){
56573         Roo.get(document).un("click", this.slideInIf, this);
56574     },
56575
56576     // these names are backwards but not changed for compat
56577     slideOut : function(){
56578         if(this.isSlid || this.el.hasActiveFx()){
56579             return;
56580         }
56581         this.isSlid = true;
56582         if(this.collapseBtn){
56583             this.collapseBtn.hide();
56584         }
56585         this.closeBtnState = this.closeBtn.getStyle('display');
56586         this.closeBtn.hide();
56587         if(this.stickBtn){
56588             this.stickBtn.show();
56589         }
56590         this.el.show();
56591         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56592         this.beforeSlide();
56593         this.el.setStyle("z-index", 10001);
56594         this.el.slideIn(this.getSlideAnchor(), {
56595             callback: function(){
56596                 this.afterSlide();
56597                 this.initAutoHide();
56598                 Roo.get(document).on("click", this.slideInIf, this);
56599                 this.fireEvent("slideshow", this);
56600             },
56601             scope: this,
56602             block: true
56603         });
56604     },
56605
56606     afterSlideIn : function(){
56607         this.clearAutoHide();
56608         this.isSlid = false;
56609         this.clearMonitor();
56610         this.el.setStyle("z-index", "");
56611         if(this.collapseBtn){
56612             this.collapseBtn.show();
56613         }
56614         this.closeBtn.setStyle('display', this.closeBtnState);
56615         if(this.stickBtn){
56616             this.stickBtn.hide();
56617         }
56618         this.fireEvent("slidehide", this);
56619     },
56620
56621     slideIn : function(cb){
56622         if(!this.isSlid || this.el.hasActiveFx()){
56623             Roo.callback(cb);
56624             return;
56625         }
56626         this.isSlid = false;
56627         this.beforeSlide();
56628         this.el.slideOut(this.getSlideAnchor(), {
56629             callback: function(){
56630                 this.el.setLeftTop(-10000, -10000);
56631                 this.afterSlide();
56632                 this.afterSlideIn();
56633                 Roo.callback(cb);
56634             },
56635             scope: this,
56636             block: true
56637         });
56638     },
56639     
56640     slideInIf : function(e){
56641         if(!e.within(this.el)){
56642             this.slideIn();
56643         }
56644     },
56645
56646     animateCollapse : function(){
56647         this.beforeSlide();
56648         this.el.setStyle("z-index", 20000);
56649         var anchor = this.getSlideAnchor();
56650         this.el.slideOut(anchor, {
56651             callback : function(){
56652                 this.el.setStyle("z-index", "");
56653                 this.collapsedEl.slideIn(anchor, {duration:.3});
56654                 this.afterSlide();
56655                 this.el.setLocation(-10000,-10000);
56656                 this.el.hide();
56657                 this.fireEvent("collapsed", this);
56658             },
56659             scope: this,
56660             block: true
56661         });
56662     },
56663
56664     animateExpand : function(){
56665         this.beforeSlide();
56666         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56667         this.el.setStyle("z-index", 20000);
56668         this.collapsedEl.hide({
56669             duration:.1
56670         });
56671         this.el.slideIn(this.getSlideAnchor(), {
56672             callback : function(){
56673                 this.el.setStyle("z-index", "");
56674                 this.afterSlide();
56675                 if(this.split){
56676                     this.split.el.show();
56677                 }
56678                 this.fireEvent("invalidated", this);
56679                 this.fireEvent("expanded", this);
56680             },
56681             scope: this,
56682             block: true
56683         });
56684     },
56685
56686     anchors : {
56687         "west" : "left",
56688         "east" : "right",
56689         "north" : "top",
56690         "south" : "bottom"
56691     },
56692
56693     sanchors : {
56694         "west" : "l",
56695         "east" : "r",
56696         "north" : "t",
56697         "south" : "b"
56698     },
56699
56700     canchors : {
56701         "west" : "tl-tr",
56702         "east" : "tr-tl",
56703         "north" : "tl-bl",
56704         "south" : "bl-tl"
56705     },
56706
56707     getAnchor : function(){
56708         return this.anchors[this.position];
56709     },
56710
56711     getCollapseAnchor : function(){
56712         return this.canchors[this.position];
56713     },
56714
56715     getSlideAnchor : function(){
56716         return this.sanchors[this.position];
56717     },
56718
56719     getAlignAdj : function(){
56720         var cm = this.cmargins;
56721         switch(this.position){
56722             case "west":
56723                 return [0, 0];
56724             break;
56725             case "east":
56726                 return [0, 0];
56727             break;
56728             case "north":
56729                 return [0, 0];
56730             break;
56731             case "south":
56732                 return [0, 0];
56733             break;
56734         }
56735     },
56736
56737     getExpandAdj : function(){
56738         var c = this.collapsedEl, cm = this.cmargins;
56739         switch(this.position){
56740             case "west":
56741                 return [-(cm.right+c.getWidth()+cm.left), 0];
56742             break;
56743             case "east":
56744                 return [cm.right+c.getWidth()+cm.left, 0];
56745             break;
56746             case "north":
56747                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56748             break;
56749             case "south":
56750                 return [0, cm.top+cm.bottom+c.getHeight()];
56751             break;
56752         }
56753     }
56754 });/*
56755  * Based on:
56756  * Ext JS Library 1.1.1
56757  * Copyright(c) 2006-2007, Ext JS, LLC.
56758  *
56759  * Originally Released Under LGPL - original licence link has changed is not relivant.
56760  *
56761  * Fork - LGPL
56762  * <script type="text/javascript">
56763  */
56764 /*
56765  * These classes are private internal classes
56766  */
56767 Roo.CenterLayoutRegion = function(mgr, config){
56768     Roo.LayoutRegion.call(this, mgr, config, "center");
56769     this.visible = true;
56770     this.minWidth = config.minWidth || 20;
56771     this.minHeight = config.minHeight || 20;
56772 };
56773
56774 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56775     hide : function(){
56776         // center panel can't be hidden
56777     },
56778     
56779     show : function(){
56780         // center panel can't be hidden
56781     },
56782     
56783     getMinWidth: function(){
56784         return this.minWidth;
56785     },
56786     
56787     getMinHeight: function(){
56788         return this.minHeight;
56789     }
56790 });
56791
56792
56793 Roo.NorthLayoutRegion = function(mgr, config){
56794     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56795     if(this.split){
56796         this.split.placement = Roo.SplitBar.TOP;
56797         this.split.orientation = Roo.SplitBar.VERTICAL;
56798         this.split.el.addClass("x-layout-split-v");
56799     }
56800     var size = config.initialSize || config.height;
56801     if(typeof size != "undefined"){
56802         this.el.setHeight(size);
56803     }
56804 };
56805 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56806     orientation: Roo.SplitBar.VERTICAL,
56807     getBox : function(){
56808         if(this.collapsed){
56809             return this.collapsedEl.getBox();
56810         }
56811         var box = this.el.getBox();
56812         if(this.split){
56813             box.height += this.split.el.getHeight();
56814         }
56815         return box;
56816     },
56817     
56818     updateBox : function(box){
56819         if(this.split && !this.collapsed){
56820             box.height -= this.split.el.getHeight();
56821             this.split.el.setLeft(box.x);
56822             this.split.el.setTop(box.y+box.height);
56823             this.split.el.setWidth(box.width);
56824         }
56825         if(this.collapsed){
56826             this.updateBody(box.width, null);
56827         }
56828         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56829     }
56830 });
56831
56832 Roo.SouthLayoutRegion = function(mgr, config){
56833     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56834     if(this.split){
56835         this.split.placement = Roo.SplitBar.BOTTOM;
56836         this.split.orientation = Roo.SplitBar.VERTICAL;
56837         this.split.el.addClass("x-layout-split-v");
56838     }
56839     var size = config.initialSize || config.height;
56840     if(typeof size != "undefined"){
56841         this.el.setHeight(size);
56842     }
56843 };
56844 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56845     orientation: Roo.SplitBar.VERTICAL,
56846     getBox : function(){
56847         if(this.collapsed){
56848             return this.collapsedEl.getBox();
56849         }
56850         var box = this.el.getBox();
56851         if(this.split){
56852             var sh = this.split.el.getHeight();
56853             box.height += sh;
56854             box.y -= sh;
56855         }
56856         return box;
56857     },
56858     
56859     updateBox : function(box){
56860         if(this.split && !this.collapsed){
56861             var sh = this.split.el.getHeight();
56862             box.height -= sh;
56863             box.y += sh;
56864             this.split.el.setLeft(box.x);
56865             this.split.el.setTop(box.y-sh);
56866             this.split.el.setWidth(box.width);
56867         }
56868         if(this.collapsed){
56869             this.updateBody(box.width, null);
56870         }
56871         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56872     }
56873 });
56874
56875 Roo.EastLayoutRegion = function(mgr, config){
56876     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56877     if(this.split){
56878         this.split.placement = Roo.SplitBar.RIGHT;
56879         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56880         this.split.el.addClass("x-layout-split-h");
56881     }
56882     var size = config.initialSize || config.width;
56883     if(typeof size != "undefined"){
56884         this.el.setWidth(size);
56885     }
56886 };
56887 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56888     orientation: Roo.SplitBar.HORIZONTAL,
56889     getBox : function(){
56890         if(this.collapsed){
56891             return this.collapsedEl.getBox();
56892         }
56893         var box = this.el.getBox();
56894         if(this.split){
56895             var sw = this.split.el.getWidth();
56896             box.width += sw;
56897             box.x -= sw;
56898         }
56899         return box;
56900     },
56901
56902     updateBox : function(box){
56903         if(this.split && !this.collapsed){
56904             var sw = this.split.el.getWidth();
56905             box.width -= sw;
56906             this.split.el.setLeft(box.x);
56907             this.split.el.setTop(box.y);
56908             this.split.el.setHeight(box.height);
56909             box.x += sw;
56910         }
56911         if(this.collapsed){
56912             this.updateBody(null, box.height);
56913         }
56914         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56915     }
56916 });
56917
56918 Roo.WestLayoutRegion = function(mgr, config){
56919     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56920     if(this.split){
56921         this.split.placement = Roo.SplitBar.LEFT;
56922         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56923         this.split.el.addClass("x-layout-split-h");
56924     }
56925     var size = config.initialSize || config.width;
56926     if(typeof size != "undefined"){
56927         this.el.setWidth(size);
56928     }
56929 };
56930 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56931     orientation: Roo.SplitBar.HORIZONTAL,
56932     getBox : function(){
56933         if(this.collapsed){
56934             return this.collapsedEl.getBox();
56935         }
56936         var box = this.el.getBox();
56937         if(this.split){
56938             box.width += this.split.el.getWidth();
56939         }
56940         return box;
56941     },
56942     
56943     updateBox : function(box){
56944         if(this.split && !this.collapsed){
56945             var sw = this.split.el.getWidth();
56946             box.width -= sw;
56947             this.split.el.setLeft(box.x+box.width);
56948             this.split.el.setTop(box.y);
56949             this.split.el.setHeight(box.height);
56950         }
56951         if(this.collapsed){
56952             this.updateBody(null, box.height);
56953         }
56954         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56955     }
56956 });
56957 /*
56958  * Based on:
56959  * Ext JS Library 1.1.1
56960  * Copyright(c) 2006-2007, Ext JS, LLC.
56961  *
56962  * Originally Released Under LGPL - original licence link has changed is not relivant.
56963  *
56964  * Fork - LGPL
56965  * <script type="text/javascript">
56966  */
56967  
56968  
56969 /*
56970  * Private internal class for reading and applying state
56971  */
56972 Roo.LayoutStateManager = function(layout){
56973      // default empty state
56974      this.state = {
56975         north: {},
56976         south: {},
56977         east: {},
56978         west: {}       
56979     };
56980 };
56981
56982 Roo.LayoutStateManager.prototype = {
56983     init : function(layout, provider){
56984         this.provider = provider;
56985         var state = provider.get(layout.id+"-layout-state");
56986         if(state){
56987             var wasUpdating = layout.isUpdating();
56988             if(!wasUpdating){
56989                 layout.beginUpdate();
56990             }
56991             for(var key in state){
56992                 if(typeof state[key] != "function"){
56993                     var rstate = state[key];
56994                     var r = layout.getRegion(key);
56995                     if(r && rstate){
56996                         if(rstate.size){
56997                             r.resizeTo(rstate.size);
56998                         }
56999                         if(rstate.collapsed == true){
57000                             r.collapse(true);
57001                         }else{
57002                             r.expand(null, true);
57003                         }
57004                     }
57005                 }
57006             }
57007             if(!wasUpdating){
57008                 layout.endUpdate();
57009             }
57010             this.state = state; 
57011         }
57012         this.layout = layout;
57013         layout.on("regionresized", this.onRegionResized, this);
57014         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57015         layout.on("regionexpanded", this.onRegionExpanded, this);
57016     },
57017     
57018     storeState : function(){
57019         this.provider.set(this.layout.id+"-layout-state", this.state);
57020     },
57021     
57022     onRegionResized : function(region, newSize){
57023         this.state[region.getPosition()].size = newSize;
57024         this.storeState();
57025     },
57026     
57027     onRegionCollapsed : function(region){
57028         this.state[region.getPosition()].collapsed = true;
57029         this.storeState();
57030     },
57031     
57032     onRegionExpanded : function(region){
57033         this.state[region.getPosition()].collapsed = false;
57034         this.storeState();
57035     }
57036 };/*
57037  * Based on:
57038  * Ext JS Library 1.1.1
57039  * Copyright(c) 2006-2007, Ext JS, LLC.
57040  *
57041  * Originally Released Under LGPL - original licence link has changed is not relivant.
57042  *
57043  * Fork - LGPL
57044  * <script type="text/javascript">
57045  */
57046 /**
57047  * @class Roo.ContentPanel
57048  * @extends Roo.util.Observable
57049  * @children Roo.form.Form Roo.JsonView Roo.View
57050  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57051  * A basic ContentPanel element.
57052  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57053  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57054  * @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
57055  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57056  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57057  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57058  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57059  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57060  * @cfg {String} title          The title for this panel
57061  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57062  * @cfg {String} url            Calls {@link #setUrl} with this value
57063  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57064  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57065  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57066  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57067  * @cfg {String}    style  Extra style to add to the content panel
57068  * @cfg {Roo.menu.Menu} menu  popup menu
57069
57070  * @constructor
57071  * Create a new ContentPanel.
57072  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57073  * @param {String/Object} config A string to set only the title or a config object
57074  * @param {String} content (optional) Set the HTML content for this panel
57075  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57076  */
57077 Roo.ContentPanel = function(el, config, content){
57078     
57079      
57080     /*
57081     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57082         config = el;
57083         el = Roo.id();
57084     }
57085     if (config && config.parentLayout) { 
57086         el = config.parentLayout.el.createChild(); 
57087     }
57088     */
57089     if(el.autoCreate){ // xtype is available if this is called from factory
57090         config = el;
57091         el = Roo.id();
57092     }
57093     this.el = Roo.get(el);
57094     if(!this.el && config && config.autoCreate){
57095         if(typeof config.autoCreate == "object"){
57096             if(!config.autoCreate.id){
57097                 config.autoCreate.id = config.id||el;
57098             }
57099             this.el = Roo.DomHelper.append(document.body,
57100                         config.autoCreate, true);
57101         }else{
57102             this.el = Roo.DomHelper.append(document.body,
57103                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57104         }
57105     }
57106     
57107     
57108     this.closable = false;
57109     this.loaded = false;
57110     this.active = false;
57111     if(typeof config == "string"){
57112         this.title = config;
57113     }else{
57114         Roo.apply(this, config);
57115     }
57116     
57117     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57118         this.wrapEl = this.el.wrap();
57119         this.toolbar.container = this.el.insertSibling(false, 'before');
57120         this.toolbar = new Roo.Toolbar(this.toolbar);
57121     }
57122     
57123     // xtype created footer. - not sure if will work as we normally have to render first..
57124     if (this.footer && !this.footer.el && this.footer.xtype) {
57125         if (!this.wrapEl) {
57126             this.wrapEl = this.el.wrap();
57127         }
57128     
57129         this.footer.container = this.wrapEl.createChild();
57130          
57131         this.footer = Roo.factory(this.footer, Roo);
57132         
57133     }
57134     
57135     if(this.resizeEl){
57136         this.resizeEl = Roo.get(this.resizeEl, true);
57137     }else{
57138         this.resizeEl = this.el;
57139     }
57140     // handle view.xtype
57141     
57142  
57143     
57144     
57145     this.addEvents({
57146         /**
57147          * @event activate
57148          * Fires when this panel is activated. 
57149          * @param {Roo.ContentPanel} this
57150          */
57151         "activate" : true,
57152         /**
57153          * @event deactivate
57154          * Fires when this panel is activated. 
57155          * @param {Roo.ContentPanel} this
57156          */
57157         "deactivate" : true,
57158
57159         /**
57160          * @event resize
57161          * Fires when this panel is resized if fitToFrame is true.
57162          * @param {Roo.ContentPanel} this
57163          * @param {Number} width The width after any component adjustments
57164          * @param {Number} height The height after any component adjustments
57165          */
57166         "resize" : true,
57167         
57168          /**
57169          * @event render
57170          * Fires when this tab is created
57171          * @param {Roo.ContentPanel} this
57172          */
57173         "render" : true
57174          
57175         
57176     });
57177     
57178
57179     
57180     
57181     if(this.autoScroll){
57182         this.resizeEl.setStyle("overflow", "auto");
57183     } else {
57184         // fix randome scrolling
57185         this.el.on('scroll', function() {
57186             Roo.log('fix random scolling');
57187             this.scrollTo('top',0); 
57188         });
57189     }
57190     content = content || this.content;
57191     if(content){
57192         this.setContent(content);
57193     }
57194     if(config && config.url){
57195         this.setUrl(this.url, this.params, this.loadOnce);
57196     }
57197     
57198     
57199     
57200     Roo.ContentPanel.superclass.constructor.call(this);
57201     
57202     if (this.view && typeof(this.view.xtype) != 'undefined') {
57203         this.view.el = this.el.appendChild(document.createElement("div"));
57204         this.view = Roo.factory(this.view); 
57205         this.view.render  &&  this.view.render(false, '');  
57206     }
57207     
57208     
57209     this.fireEvent('render', this);
57210 };
57211
57212 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57213     tabTip:'',
57214     setRegion : function(region){
57215         this.region = region;
57216         if(region){
57217            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57218         }else{
57219            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57220         } 
57221     },
57222     
57223     /**
57224      * Returns the toolbar for this Panel if one was configured. 
57225      * @return {Roo.Toolbar} 
57226      */
57227     getToolbar : function(){
57228         return this.toolbar;
57229     },
57230     
57231     setActiveState : function(active){
57232         this.active = active;
57233         if(!active){
57234             this.fireEvent("deactivate", this);
57235         }else{
57236             this.fireEvent("activate", this);
57237         }
57238     },
57239     /**
57240      * Updates this panel's element
57241      * @param {String} content The new content
57242      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57243     */
57244     setContent : function(content, loadScripts){
57245         this.el.update(content, loadScripts);
57246     },
57247
57248     ignoreResize : function(w, h){
57249         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57250             return true;
57251         }else{
57252             this.lastSize = {width: w, height: h};
57253             return false;
57254         }
57255     },
57256     /**
57257      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57258      * @return {Roo.UpdateManager} The UpdateManager
57259      */
57260     getUpdateManager : function(){
57261         return this.el.getUpdateManager();
57262     },
57263      /**
57264      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57265      * @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:
57266 <pre><code>
57267 panel.load({
57268     url: "your-url.php",
57269     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57270     callback: yourFunction,
57271     scope: yourObject, //(optional scope)
57272     discardUrl: false,
57273     nocache: false,
57274     text: "Loading...",
57275     timeout: 30,
57276     scripts: false
57277 });
57278 </code></pre>
57279      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57280      * 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.
57281      * @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}
57282      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57283      * @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.
57284      * @return {Roo.ContentPanel} this
57285      */
57286     load : function(){
57287         var um = this.el.getUpdateManager();
57288         um.update.apply(um, arguments);
57289         return this;
57290     },
57291
57292
57293     /**
57294      * 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.
57295      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57296      * @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)
57297      * @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)
57298      * @return {Roo.UpdateManager} The UpdateManager
57299      */
57300     setUrl : function(url, params, loadOnce){
57301         if(this.refreshDelegate){
57302             this.removeListener("activate", this.refreshDelegate);
57303         }
57304         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57305         this.on("activate", this.refreshDelegate);
57306         return this.el.getUpdateManager();
57307     },
57308     
57309     _handleRefresh : function(url, params, loadOnce){
57310         if(!loadOnce || !this.loaded){
57311             var updater = this.el.getUpdateManager();
57312             updater.update(url, params, this._setLoaded.createDelegate(this));
57313         }
57314     },
57315     
57316     _setLoaded : function(){
57317         this.loaded = true;
57318     }, 
57319     
57320     /**
57321      * Returns this panel's id
57322      * @return {String} 
57323      */
57324     getId : function(){
57325         return this.el.id;
57326     },
57327     
57328     /** 
57329      * Returns this panel's element - used by regiosn to add.
57330      * @return {Roo.Element} 
57331      */
57332     getEl : function(){
57333         return this.wrapEl || this.el;
57334     },
57335     
57336     adjustForComponents : function(width, height)
57337     {
57338         //Roo.log('adjustForComponents ');
57339         if(this.resizeEl != this.el){
57340             width -= this.el.getFrameWidth('lr');
57341             height -= this.el.getFrameWidth('tb');
57342         }
57343         if(this.toolbar){
57344             var te = this.toolbar.getEl();
57345             height -= te.getHeight();
57346             te.setWidth(width);
57347         }
57348         if(this.footer){
57349             var te = this.footer.getEl();
57350             //Roo.log("footer:" + te.getHeight());
57351             
57352             height -= te.getHeight();
57353             te.setWidth(width);
57354         }
57355         
57356         
57357         if(this.adjustments){
57358             width += this.adjustments[0];
57359             height += this.adjustments[1];
57360         }
57361         return {"width": width, "height": height};
57362     },
57363     
57364     setSize : function(width, height){
57365         if(this.fitToFrame && !this.ignoreResize(width, height)){
57366             if(this.fitContainer && this.resizeEl != this.el){
57367                 this.el.setSize(width, height);
57368             }
57369             var size = this.adjustForComponents(width, height);
57370             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57371             this.fireEvent('resize', this, size.width, size.height);
57372         }
57373     },
57374     
57375     /**
57376      * Returns this panel's title
57377      * @return {String} 
57378      */
57379     getTitle : function(){
57380         return this.title;
57381     },
57382     
57383     /**
57384      * Set this panel's title
57385      * @param {String} title
57386      */
57387     setTitle : function(title){
57388         this.title = title;
57389         if(this.region){
57390             this.region.updatePanelTitle(this, title);
57391         }
57392     },
57393     
57394     /**
57395      * Returns true is this panel was configured to be closable
57396      * @return {Boolean} 
57397      */
57398     isClosable : function(){
57399         return this.closable;
57400     },
57401     
57402     beforeSlide : function(){
57403         this.el.clip();
57404         this.resizeEl.clip();
57405     },
57406     
57407     afterSlide : function(){
57408         this.el.unclip();
57409         this.resizeEl.unclip();
57410     },
57411     
57412     /**
57413      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57414      *   Will fail silently if the {@link #setUrl} method has not been called.
57415      *   This does not activate the panel, just updates its content.
57416      */
57417     refresh : function(){
57418         if(this.refreshDelegate){
57419            this.loaded = false;
57420            this.refreshDelegate();
57421         }
57422     },
57423     
57424     /**
57425      * Destroys this panel
57426      */
57427     destroy : function(){
57428         this.el.removeAllListeners();
57429         var tempEl = document.createElement("span");
57430         tempEl.appendChild(this.el.dom);
57431         tempEl.innerHTML = "";
57432         this.el.remove();
57433         this.el = null;
57434     },
57435     
57436     /**
57437      * form - if the content panel contains a form - this is a reference to it.
57438      * @type {Roo.form.Form}
57439      */
57440     form : false,
57441     /**
57442      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57443      *    This contains a reference to it.
57444      * @type {Roo.View}
57445      */
57446     view : false,
57447     
57448       /**
57449      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57450      * <pre><code>
57451
57452 layout.addxtype({
57453        xtype : 'Form',
57454        items: [ .... ]
57455    }
57456 );
57457
57458 </code></pre>
57459      * @param {Object} cfg Xtype definition of item to add.
57460      */
57461     
57462     addxtype : function(cfg) {
57463         // add form..
57464         if (cfg.xtype.match(/^Form$/)) {
57465             
57466             var el;
57467             //if (this.footer) {
57468             //    el = this.footer.container.insertSibling(false, 'before');
57469             //} else {
57470                 el = this.el.createChild();
57471             //}
57472
57473             this.form = new  Roo.form.Form(cfg);
57474             
57475             
57476             if ( this.form.allItems.length) {
57477                 this.form.render(el.dom);
57478             }
57479             return this.form;
57480         }
57481         // should only have one of theses..
57482         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57483             // views.. should not be just added - used named prop 'view''
57484             
57485             cfg.el = this.el.appendChild(document.createElement("div"));
57486             // factory?
57487             
57488             var ret = new Roo.factory(cfg);
57489              
57490              ret.render && ret.render(false, ''); // render blank..
57491             this.view = ret;
57492             return ret;
57493         }
57494         return false;
57495     }
57496 });
57497
57498 /**
57499  * @class Roo.GridPanel
57500  * @extends Roo.ContentPanel
57501  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57502  * @constructor
57503  * Create a new GridPanel.
57504  * @cfg {Roo.grid.Grid} grid The grid for this panel
57505  */
57506 Roo.GridPanel = function(grid, config){
57507     
57508     // universal ctor...
57509     if (typeof(grid.grid) != 'undefined') {
57510         config = grid;
57511         grid = config.grid;
57512     }
57513     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57514         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57515         
57516     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57517     
57518     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57519     
57520     if(this.toolbar){
57521         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57522     }
57523     // xtype created footer. - not sure if will work as we normally have to render first..
57524     if (this.footer && !this.footer.el && this.footer.xtype) {
57525         
57526         this.footer.container = this.grid.getView().getFooterPanel(true);
57527         this.footer.dataSource = this.grid.dataSource;
57528         this.footer = Roo.factory(this.footer, Roo);
57529         
57530     }
57531     
57532     grid.monitorWindowResize = false; // turn off autosizing
57533     grid.autoHeight = false;
57534     grid.autoWidth = false;
57535     this.grid = grid;
57536     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57537 };
57538
57539 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57540     getId : function(){
57541         return this.grid.id;
57542     },
57543     
57544     /**
57545      * Returns the grid for this panel
57546      * @return {Roo.grid.Grid} 
57547      */
57548     getGrid : function(){
57549         return this.grid;    
57550     },
57551     
57552     setSize : function(width, height){
57553         if(!this.ignoreResize(width, height)){
57554             var grid = this.grid;
57555             var size = this.adjustForComponents(width, height);
57556             grid.getGridEl().setSize(size.width, size.height);
57557             grid.autoSize();
57558         }
57559     },
57560     
57561     beforeSlide : function(){
57562         this.grid.getView().scroller.clip();
57563     },
57564     
57565     afterSlide : function(){
57566         this.grid.getView().scroller.unclip();
57567     },
57568     
57569     destroy : function(){
57570         this.grid.destroy();
57571         delete this.grid;
57572         Roo.GridPanel.superclass.destroy.call(this); 
57573     }
57574 });
57575
57576
57577 /**
57578  * @class Roo.NestedLayoutPanel
57579  * @extends Roo.ContentPanel
57580  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57581  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57582  *
57583  * 
57584  * @constructor
57585  * Create a new NestedLayoutPanel.
57586  * 
57587  * 
57588  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57589  * @param {String/Object} config A string to set only the title or a config object
57590  */
57591 Roo.NestedLayoutPanel = function(layout, config)
57592 {
57593     // construct with only one argument..
57594     /* FIXME - implement nicer consturctors
57595     if (layout.layout) {
57596         config = layout;
57597         layout = config.layout;
57598         delete config.layout;
57599     }
57600     if (layout.xtype && !layout.getEl) {
57601         // then layout needs constructing..
57602         layout = Roo.factory(layout, Roo);
57603     }
57604     */
57605     
57606     
57607     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57608     
57609     layout.monitorWindowResize = false; // turn off autosizing
57610     this.layout = layout;
57611     this.layout.getEl().addClass("x-layout-nested-layout");
57612     
57613     
57614     
57615     
57616 };
57617
57618 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57619
57620     setSize : function(width, height){
57621         if(!this.ignoreResize(width, height)){
57622             var size = this.adjustForComponents(width, height);
57623             var el = this.layout.getEl();
57624             el.setSize(size.width, size.height);
57625             var touch = el.dom.offsetWidth;
57626             this.layout.layout();
57627             // ie requires a double layout on the first pass
57628             if(Roo.isIE && !this.initialized){
57629                 this.initialized = true;
57630                 this.layout.layout();
57631             }
57632         }
57633     },
57634     
57635     // activate all subpanels if not currently active..
57636     
57637     setActiveState : function(active){
57638         this.active = active;
57639         if(!active){
57640             this.fireEvent("deactivate", this);
57641             return;
57642         }
57643         
57644         this.fireEvent("activate", this);
57645         // not sure if this should happen before or after..
57646         if (!this.layout) {
57647             return; // should not happen..
57648         }
57649         var reg = false;
57650         for (var r in this.layout.regions) {
57651             reg = this.layout.getRegion(r);
57652             if (reg.getActivePanel()) {
57653                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57654                 reg.setActivePanel(reg.getActivePanel());
57655                 continue;
57656             }
57657             if (!reg.panels.length) {
57658                 continue;
57659             }
57660             reg.showPanel(reg.getPanel(0));
57661         }
57662         
57663         
57664         
57665         
57666     },
57667     
57668     /**
57669      * Returns the nested BorderLayout for this panel
57670      * @return {Roo.BorderLayout} 
57671      */
57672     getLayout : function(){
57673         return this.layout;
57674     },
57675     
57676      /**
57677      * Adds a xtype elements to the layout of the nested panel
57678      * <pre><code>
57679
57680 panel.addxtype({
57681        xtype : 'ContentPanel',
57682        region: 'west',
57683        items: [ .... ]
57684    }
57685 );
57686
57687 panel.addxtype({
57688         xtype : 'NestedLayoutPanel',
57689         region: 'west',
57690         layout: {
57691            center: { },
57692            west: { }   
57693         },
57694         items : [ ... list of content panels or nested layout panels.. ]
57695    }
57696 );
57697 </code></pre>
57698      * @param {Object} cfg Xtype definition of item to add.
57699      */
57700     addxtype : function(cfg) {
57701         return this.layout.addxtype(cfg);
57702     
57703     }
57704 });
57705
57706 Roo.ScrollPanel = function(el, config, content){
57707     config = config || {};
57708     config.fitToFrame = true;
57709     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57710     
57711     this.el.dom.style.overflow = "hidden";
57712     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57713     this.el.removeClass("x-layout-inactive-content");
57714     this.el.on("mousewheel", this.onWheel, this);
57715
57716     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57717     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57718     up.unselectable(); down.unselectable();
57719     up.on("click", this.scrollUp, this);
57720     down.on("click", this.scrollDown, this);
57721     up.addClassOnOver("x-scroller-btn-over");
57722     down.addClassOnOver("x-scroller-btn-over");
57723     up.addClassOnClick("x-scroller-btn-click");
57724     down.addClassOnClick("x-scroller-btn-click");
57725     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57726
57727     this.resizeEl = this.el;
57728     this.el = wrap; this.up = up; this.down = down;
57729 };
57730
57731 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57732     increment : 100,
57733     wheelIncrement : 5,
57734     scrollUp : function(){
57735         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57736     },
57737
57738     scrollDown : function(){
57739         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57740     },
57741
57742     afterScroll : function(){
57743         var el = this.resizeEl;
57744         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57745         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57746         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57747     },
57748
57749     setSize : function(){
57750         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57751         this.afterScroll();
57752     },
57753
57754     onWheel : function(e){
57755         var d = e.getWheelDelta();
57756         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57757         this.afterScroll();
57758         e.stopEvent();
57759     },
57760
57761     setContent : function(content, loadScripts){
57762         this.resizeEl.update(content, loadScripts);
57763     }
57764
57765 });
57766
57767
57768
57769 /**
57770  * @class Roo.TreePanel
57771  * @extends Roo.ContentPanel
57772  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57773  * Treepanel component
57774  * 
57775  * @constructor
57776  * Create a new TreePanel. - defaults to fit/scoll contents.
57777  * @param {String/Object} config A string to set only the panel's title, or a config object
57778  */
57779 Roo.TreePanel = function(config){
57780     var el = config.el;
57781     var tree = config.tree;
57782     delete config.tree; 
57783     delete config.el; // hopefull!
57784     
57785     // wrapper for IE7 strict & safari scroll issue
57786     
57787     var treeEl = el.createChild();
57788     config.resizeEl = treeEl;
57789     
57790     
57791     
57792     Roo.TreePanel.superclass.constructor.call(this, el, config);
57793  
57794  
57795     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57796     //console.log(tree);
57797     this.on('activate', function()
57798     {
57799         if (this.tree.rendered) {
57800             return;
57801         }
57802         //console.log('render tree');
57803         this.tree.render();
57804     });
57805     // this should not be needed.. - it's actually the 'el' that resizes?
57806     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57807     
57808     //this.on('resize',  function (cp, w, h) {
57809     //        this.tree.innerCt.setWidth(w);
57810     //        this.tree.innerCt.setHeight(h);
57811     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57812     //});
57813
57814         
57815     
57816 };
57817
57818 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57819     fitToFrame : true,
57820     autoScroll : true,
57821     /*
57822      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57823      */
57824     tree : false
57825
57826 });
57827
57828
57829
57830
57831
57832
57833
57834
57835
57836
57837
57838 /*
57839  * Based on:
57840  * Ext JS Library 1.1.1
57841  * Copyright(c) 2006-2007, Ext JS, LLC.
57842  *
57843  * Originally Released Under LGPL - original licence link has changed is not relivant.
57844  *
57845  * Fork - LGPL
57846  * <script type="text/javascript">
57847  */
57848  
57849
57850 /**
57851  * @class Roo.ReaderLayout
57852  * @extends Roo.BorderLayout
57853  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57854  * center region containing two nested regions (a top one for a list view and one for item preview below),
57855  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57856  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57857  * expedites the setup of the overall layout and regions for this common application style.
57858  * Example:
57859  <pre><code>
57860 var reader = new Roo.ReaderLayout();
57861 var CP = Roo.ContentPanel;  // shortcut for adding
57862
57863 reader.beginUpdate();
57864 reader.add("north", new CP("north", "North"));
57865 reader.add("west", new CP("west", {title: "West"}));
57866 reader.add("east", new CP("east", {title: "East"}));
57867
57868 reader.regions.listView.add(new CP("listView", "List"));
57869 reader.regions.preview.add(new CP("preview", "Preview"));
57870 reader.endUpdate();
57871 </code></pre>
57872 * @constructor
57873 * Create a new ReaderLayout
57874 * @param {Object} config Configuration options
57875 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57876 * document.body if omitted)
57877 */
57878 Roo.ReaderLayout = function(config, renderTo){
57879     var c = config || {size:{}};
57880     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57881         north: c.north !== false ? Roo.apply({
57882             split:false,
57883             initialSize: 32,
57884             titlebar: false
57885         }, c.north) : false,
57886         west: c.west !== false ? Roo.apply({
57887             split:true,
57888             initialSize: 200,
57889             minSize: 175,
57890             maxSize: 400,
57891             titlebar: true,
57892             collapsible: true,
57893             animate: true,
57894             margins:{left:5,right:0,bottom:5,top:5},
57895             cmargins:{left:5,right:5,bottom:5,top:5}
57896         }, c.west) : false,
57897         east: c.east !== false ? Roo.apply({
57898             split:true,
57899             initialSize: 200,
57900             minSize: 175,
57901             maxSize: 400,
57902             titlebar: true,
57903             collapsible: true,
57904             animate: true,
57905             margins:{left:0,right:5,bottom:5,top:5},
57906             cmargins:{left:5,right:5,bottom:5,top:5}
57907         }, c.east) : false,
57908         center: Roo.apply({
57909             tabPosition: 'top',
57910             autoScroll:false,
57911             closeOnTab: true,
57912             titlebar:false,
57913             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57914         }, c.center)
57915     });
57916
57917     this.el.addClass('x-reader');
57918
57919     this.beginUpdate();
57920
57921     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57922         south: c.preview !== false ? Roo.apply({
57923             split:true,
57924             initialSize: 200,
57925             minSize: 100,
57926             autoScroll:true,
57927             collapsible:true,
57928             titlebar: true,
57929             cmargins:{top:5,left:0, right:0, bottom:0}
57930         }, c.preview) : false,
57931         center: Roo.apply({
57932             autoScroll:false,
57933             titlebar:false,
57934             minHeight:200
57935         }, c.listView)
57936     });
57937     this.add('center', new Roo.NestedLayoutPanel(inner,
57938             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57939
57940     this.endUpdate();
57941
57942     this.regions.preview = inner.getRegion('south');
57943     this.regions.listView = inner.getRegion('center');
57944 };
57945
57946 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57947  * Based on:
57948  * Ext JS Library 1.1.1
57949  * Copyright(c) 2006-2007, Ext JS, LLC.
57950  *
57951  * Originally Released Under LGPL - original licence link has changed is not relivant.
57952  *
57953  * Fork - LGPL
57954  * <script type="text/javascript">
57955  */
57956  
57957 /**
57958  * @class Roo.grid.Grid
57959  * @extends Roo.util.Observable
57960  * This class represents the primary interface of a component based grid control.
57961  * <br><br>Usage:<pre><code>
57962  var grid = new Roo.grid.Grid("my-container-id", {
57963      ds: myDataStore,
57964      cm: myColModel,
57965      selModel: mySelectionModel,
57966      autoSizeColumns: true,
57967      monitorWindowResize: false,
57968      trackMouseOver: true
57969  });
57970  // set any options
57971  grid.render();
57972  * </code></pre>
57973  * <b>Common Problems:</b><br/>
57974  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57975  * element will correct this<br/>
57976  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57977  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57978  * are unpredictable.<br/>
57979  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57980  * grid to calculate dimensions/offsets.<br/>
57981   * @constructor
57982  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57983  * The container MUST have some type of size defined for the grid to fill. The container will be
57984  * automatically set to position relative if it isn't already.
57985  * @param {Object} config A config object that sets properties on this grid.
57986  */
57987 Roo.grid.Grid = function(container, config){
57988         // initialize the container
57989         this.container = Roo.get(container);
57990         this.container.update("");
57991         this.container.setStyle("overflow", "hidden");
57992     this.container.addClass('x-grid-container');
57993
57994     this.id = this.container.id;
57995
57996     Roo.apply(this, config);
57997     // check and correct shorthanded configs
57998     if(this.ds){
57999         this.dataSource = this.ds;
58000         delete this.ds;
58001     }
58002     if(this.cm){
58003         this.colModel = this.cm;
58004         delete this.cm;
58005     }
58006     if(this.sm){
58007         this.selModel = this.sm;
58008         delete this.sm;
58009     }
58010
58011     if (this.selModel) {
58012         this.selModel = Roo.factory(this.selModel, Roo.grid);
58013         this.sm = this.selModel;
58014         this.sm.xmodule = this.xmodule || false;
58015     }
58016     if (typeof(this.colModel.config) == 'undefined') {
58017         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58018         this.cm = this.colModel;
58019         this.cm.xmodule = this.xmodule || false;
58020     }
58021     if (this.dataSource) {
58022         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58023         this.ds = this.dataSource;
58024         this.ds.xmodule = this.xmodule || false;
58025          
58026     }
58027     
58028     
58029     
58030     if(this.width){
58031         this.container.setWidth(this.width);
58032     }
58033
58034     if(this.height){
58035         this.container.setHeight(this.height);
58036     }
58037     /** @private */
58038         this.addEvents({
58039         // raw events
58040         /**
58041          * @event click
58042          * The raw click event for the entire grid.
58043          * @param {Roo.EventObject} e
58044          */
58045         "click" : true,
58046         /**
58047          * @event dblclick
58048          * The raw dblclick event for the entire grid.
58049          * @param {Roo.EventObject} e
58050          */
58051         "dblclick" : true,
58052         /**
58053          * @event contextmenu
58054          * The raw contextmenu event for the entire grid.
58055          * @param {Roo.EventObject} e
58056          */
58057         "contextmenu" : true,
58058         /**
58059          * @event mousedown
58060          * The raw mousedown event for the entire grid.
58061          * @param {Roo.EventObject} e
58062          */
58063         "mousedown" : true,
58064         /**
58065          * @event mouseup
58066          * The raw mouseup event for the entire grid.
58067          * @param {Roo.EventObject} e
58068          */
58069         "mouseup" : true,
58070         /**
58071          * @event mouseover
58072          * The raw mouseover event for the entire grid.
58073          * @param {Roo.EventObject} e
58074          */
58075         "mouseover" : true,
58076         /**
58077          * @event mouseout
58078          * The raw mouseout event for the entire grid.
58079          * @param {Roo.EventObject} e
58080          */
58081         "mouseout" : true,
58082         /**
58083          * @event keypress
58084          * The raw keypress event for the entire grid.
58085          * @param {Roo.EventObject} e
58086          */
58087         "keypress" : true,
58088         /**
58089          * @event keydown
58090          * The raw keydown event for the entire grid.
58091          * @param {Roo.EventObject} e
58092          */
58093         "keydown" : true,
58094
58095         // custom events
58096
58097         /**
58098          * @event cellclick
58099          * Fires when a cell is clicked
58100          * @param {Grid} this
58101          * @param {Number} rowIndex
58102          * @param {Number} columnIndex
58103          * @param {Roo.EventObject} e
58104          */
58105         "cellclick" : true,
58106         /**
58107          * @event celldblclick
58108          * Fires when a cell is double clicked
58109          * @param {Grid} this
58110          * @param {Number} rowIndex
58111          * @param {Number} columnIndex
58112          * @param {Roo.EventObject} e
58113          */
58114         "celldblclick" : true,
58115         /**
58116          * @event rowclick
58117          * Fires when a row is clicked
58118          * @param {Grid} this
58119          * @param {Number} rowIndex
58120          * @param {Roo.EventObject} e
58121          */
58122         "rowclick" : true,
58123         /**
58124          * @event rowdblclick
58125          * Fires when a row is double clicked
58126          * @param {Grid} this
58127          * @param {Number} rowIndex
58128          * @param {Roo.EventObject} e
58129          */
58130         "rowdblclick" : true,
58131         /**
58132          * @event headerclick
58133          * Fires when a header is clicked
58134          * @param {Grid} this
58135          * @param {Number} columnIndex
58136          * @param {Roo.EventObject} e
58137          */
58138         "headerclick" : true,
58139         /**
58140          * @event headerdblclick
58141          * Fires when a header cell is double clicked
58142          * @param {Grid} this
58143          * @param {Number} columnIndex
58144          * @param {Roo.EventObject} e
58145          */
58146         "headerdblclick" : true,
58147         /**
58148          * @event rowcontextmenu
58149          * Fires when a row is right clicked
58150          * @param {Grid} this
58151          * @param {Number} rowIndex
58152          * @param {Roo.EventObject} e
58153          */
58154         "rowcontextmenu" : true,
58155         /**
58156          * @event cellcontextmenu
58157          * Fires when a cell is right clicked
58158          * @param {Grid} this
58159          * @param {Number} rowIndex
58160          * @param {Number} cellIndex
58161          * @param {Roo.EventObject} e
58162          */
58163          "cellcontextmenu" : true,
58164         /**
58165          * @event headercontextmenu
58166          * Fires when a header is right clicked
58167          * @param {Grid} this
58168          * @param {Number} columnIndex
58169          * @param {Roo.EventObject} e
58170          */
58171         "headercontextmenu" : true,
58172         /**
58173          * @event bodyscroll
58174          * Fires when the body element is scrolled
58175          * @param {Number} scrollLeft
58176          * @param {Number} scrollTop
58177          */
58178         "bodyscroll" : true,
58179         /**
58180          * @event columnresize
58181          * Fires when the user resizes a column
58182          * @param {Number} columnIndex
58183          * @param {Number} newSize
58184          */
58185         "columnresize" : true,
58186         /**
58187          * @event columnmove
58188          * Fires when the user moves a column
58189          * @param {Number} oldIndex
58190          * @param {Number} newIndex
58191          */
58192         "columnmove" : true,
58193         /**
58194          * @event startdrag
58195          * Fires when row(s) start being dragged
58196          * @param {Grid} this
58197          * @param {Roo.GridDD} dd The drag drop object
58198          * @param {event} e The raw browser event
58199          */
58200         "startdrag" : true,
58201         /**
58202          * @event enddrag
58203          * Fires when a drag operation is complete
58204          * @param {Grid} this
58205          * @param {Roo.GridDD} dd The drag drop object
58206          * @param {event} e The raw browser event
58207          */
58208         "enddrag" : true,
58209         /**
58210          * @event dragdrop
58211          * Fires when dragged row(s) are dropped on a valid DD target
58212          * @param {Grid} this
58213          * @param {Roo.GridDD} dd The drag drop object
58214          * @param {String} targetId The target drag drop object
58215          * @param {event} e The raw browser event
58216          */
58217         "dragdrop" : true,
58218         /**
58219          * @event dragover
58220          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58221          * @param {Grid} this
58222          * @param {Roo.GridDD} dd The drag drop object
58223          * @param {String} targetId The target drag drop object
58224          * @param {event} e The raw browser event
58225          */
58226         "dragover" : true,
58227         /**
58228          * @event dragenter
58229          *  Fires when the dragged row(s) first cross another DD target while being dragged
58230          * @param {Grid} this
58231          * @param {Roo.GridDD} dd The drag drop object
58232          * @param {String} targetId The target drag drop object
58233          * @param {event} e The raw browser event
58234          */
58235         "dragenter" : true,
58236         /**
58237          * @event dragout
58238          * Fires when the dragged row(s) leave another DD target while being dragged
58239          * @param {Grid} this
58240          * @param {Roo.GridDD} dd The drag drop object
58241          * @param {String} targetId The target drag drop object
58242          * @param {event} e The raw browser event
58243          */
58244         "dragout" : true,
58245         /**
58246          * @event rowclass
58247          * Fires when a row is rendered, so you can change add a style to it.
58248          * @param {GridView} gridview   The grid view
58249          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58250          */
58251         'rowclass' : true,
58252
58253         /**
58254          * @event render
58255          * Fires when the grid is rendered
58256          * @param {Grid} grid
58257          */
58258         'render' : true
58259     });
58260
58261     Roo.grid.Grid.superclass.constructor.call(this);
58262 };
58263 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58264     
58265     /**
58266          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58267          */
58268         /**
58269          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58270          */
58271         /**
58272          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58273          */
58274         /**
58275          * @cfg {Roo.grid.Store} ds The data store for the grid
58276          */
58277         /**
58278          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58279          */
58280         /**
58281      * @cfg {String} ddGroup - drag drop group.
58282      */
58283       /**
58284      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58285      */
58286
58287     /**
58288      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58289      */
58290     minColumnWidth : 25,
58291
58292     /**
58293      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58294      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58295      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58296      */
58297     autoSizeColumns : false,
58298
58299     /**
58300      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58301      */
58302     autoSizeHeaders : true,
58303
58304     /**
58305      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58306      */
58307     monitorWindowResize : true,
58308
58309     /**
58310      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58311      * rows measured to get a columns size. Default is 0 (all rows).
58312      */
58313     maxRowsToMeasure : 0,
58314
58315     /**
58316      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58317      */
58318     trackMouseOver : true,
58319
58320     /**
58321     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58322     */
58323       /**
58324     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58325     */
58326     
58327     /**
58328     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58329     */
58330     enableDragDrop : false,
58331     
58332     /**
58333     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58334     */
58335     enableColumnMove : true,
58336     
58337     /**
58338     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58339     */
58340     enableColumnHide : true,
58341     
58342     /**
58343     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58344     */
58345     enableRowHeightSync : false,
58346     
58347     /**
58348     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58349     */
58350     stripeRows : true,
58351     
58352     /**
58353     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58354     */
58355     autoHeight : false,
58356
58357     /**
58358      * @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.
58359      */
58360     autoExpandColumn : false,
58361
58362     /**
58363     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58364     * Default is 50.
58365     */
58366     autoExpandMin : 50,
58367
58368     /**
58369     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58370     */
58371     autoExpandMax : 1000,
58372
58373     /**
58374     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58375     */
58376     view : null,
58377
58378     /**
58379     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58380     */
58381     loadMask : false,
58382     /**
58383     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58384     */
58385     dropTarget: false,
58386      /**
58387     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58388     */ 
58389     sortColMenu : false,
58390     
58391     // private
58392     rendered : false,
58393
58394     /**
58395     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58396     * of a fixed width. Default is false.
58397     */
58398     /**
58399     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58400     */
58401     
58402     
58403     /**
58404     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58405     * %0 is replaced with the number of selected rows.
58406     */
58407     ddText : "{0} selected row{1}",
58408     
58409     
58410     /**
58411      * Called once after all setup has been completed and the grid is ready to be rendered.
58412      * @return {Roo.grid.Grid} this
58413      */
58414     render : function()
58415     {
58416         var c = this.container;
58417         // try to detect autoHeight/width mode
58418         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58419             this.autoHeight = true;
58420         }
58421         var view = this.getView();
58422         view.init(this);
58423
58424         c.on("click", this.onClick, this);
58425         c.on("dblclick", this.onDblClick, this);
58426         c.on("contextmenu", this.onContextMenu, this);
58427         c.on("keydown", this.onKeyDown, this);
58428         if (Roo.isTouch) {
58429             c.on("touchstart", this.onTouchStart, this);
58430         }
58431
58432         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58433
58434         this.getSelectionModel().init(this);
58435
58436         view.render();
58437
58438         if(this.loadMask){
58439             this.loadMask = new Roo.LoadMask(this.container,
58440                     Roo.apply({store:this.dataSource}, this.loadMask));
58441         }
58442         
58443         
58444         if (this.toolbar && this.toolbar.xtype) {
58445             this.toolbar.container = this.getView().getHeaderPanel(true);
58446             this.toolbar = new Roo.Toolbar(this.toolbar);
58447         }
58448         if (this.footer && this.footer.xtype) {
58449             this.footer.dataSource = this.getDataSource();
58450             this.footer.container = this.getView().getFooterPanel(true);
58451             this.footer = Roo.factory(this.footer, Roo);
58452         }
58453         if (this.dropTarget && this.dropTarget.xtype) {
58454             delete this.dropTarget.xtype;
58455             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58456         }
58457         
58458         
58459         this.rendered = true;
58460         this.fireEvent('render', this);
58461         return this;
58462     },
58463
58464     /**
58465      * Reconfigures the grid to use a different Store and Column Model.
58466      * The View will be bound to the new objects and refreshed.
58467      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58468      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58469      */
58470     reconfigure : function(dataSource, colModel){
58471         if(this.loadMask){
58472             this.loadMask.destroy();
58473             this.loadMask = new Roo.LoadMask(this.container,
58474                     Roo.apply({store:dataSource}, this.loadMask));
58475         }
58476         this.view.bind(dataSource, colModel);
58477         this.dataSource = dataSource;
58478         this.colModel = colModel;
58479         this.view.refresh(true);
58480     },
58481     /**
58482      * addColumns
58483      * Add's a column, default at the end..
58484      
58485      * @param {int} position to add (default end)
58486      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58487      */
58488     addColumns : function(pos, ar)
58489     {
58490         
58491         for (var i =0;i< ar.length;i++) {
58492             var cfg = ar[i];
58493             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58494             this.cm.lookup[cfg.id] = cfg;
58495         }
58496         
58497         
58498         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58499             pos = this.cm.config.length; //this.cm.config.push(cfg);
58500         } 
58501         pos = Math.max(0,pos);
58502         ar.unshift(0);
58503         ar.unshift(pos);
58504         this.cm.config.splice.apply(this.cm.config, ar);
58505         
58506         
58507         
58508         this.view.generateRules(this.cm);
58509         this.view.refresh(true);
58510         
58511     },
58512     
58513     
58514     
58515     
58516     // private
58517     onKeyDown : function(e){
58518         this.fireEvent("keydown", e);
58519     },
58520
58521     /**
58522      * Destroy this grid.
58523      * @param {Boolean} removeEl True to remove the element
58524      */
58525     destroy : function(removeEl, keepListeners){
58526         if(this.loadMask){
58527             this.loadMask.destroy();
58528         }
58529         var c = this.container;
58530         c.removeAllListeners();
58531         this.view.destroy();
58532         this.colModel.purgeListeners();
58533         if(!keepListeners){
58534             this.purgeListeners();
58535         }
58536         c.update("");
58537         if(removeEl === true){
58538             c.remove();
58539         }
58540     },
58541
58542     // private
58543     processEvent : function(name, e){
58544         // does this fire select???
58545         //Roo.log('grid:processEvent '  + name);
58546         
58547         if (name != 'touchstart' ) {
58548             this.fireEvent(name, e);    
58549         }
58550         
58551         var t = e.getTarget();
58552         var v = this.view;
58553         var header = v.findHeaderIndex(t);
58554         if(header !== false){
58555             var ename = name == 'touchstart' ? 'click' : name;
58556              
58557             this.fireEvent("header" + ename, this, header, e);
58558         }else{
58559             var row = v.findRowIndex(t);
58560             var cell = v.findCellIndex(t);
58561             if (name == 'touchstart') {
58562                 // first touch is always a click.
58563                 // hopefull this happens after selection is updated.?
58564                 name = false;
58565                 
58566                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58567                     var cs = this.selModel.getSelectedCell();
58568                     if (row == cs[0] && cell == cs[1]){
58569                         name = 'dblclick';
58570                     }
58571                 }
58572                 if (typeof(this.selModel.getSelections) != 'undefined') {
58573                     var cs = this.selModel.getSelections();
58574                     var ds = this.dataSource;
58575                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58576                         name = 'dblclick';
58577                     }
58578                 }
58579                 if (!name) {
58580                     return;
58581                 }
58582             }
58583             
58584             
58585             if(row !== false){
58586                 this.fireEvent("row" + name, this, row, e);
58587                 if(cell !== false){
58588                     this.fireEvent("cell" + name, this, row, cell, e);
58589                 }
58590             }
58591         }
58592     },
58593
58594     // private
58595     onClick : function(e){
58596         this.processEvent("click", e);
58597     },
58598    // private
58599     onTouchStart : function(e){
58600         this.processEvent("touchstart", e);
58601     },
58602
58603     // private
58604     onContextMenu : function(e, t){
58605         this.processEvent("contextmenu", e);
58606     },
58607
58608     // private
58609     onDblClick : function(e){
58610         this.processEvent("dblclick", e);
58611     },
58612
58613     // private
58614     walkCells : function(row, col, step, fn, scope){
58615         var cm = this.colModel, clen = cm.getColumnCount();
58616         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58617         if(step < 0){
58618             if(col < 0){
58619                 row--;
58620                 first = false;
58621             }
58622             while(row >= 0){
58623                 if(!first){
58624                     col = clen-1;
58625                 }
58626                 first = false;
58627                 while(col >= 0){
58628                     if(fn.call(scope || this, row, col, cm) === true){
58629                         return [row, col];
58630                     }
58631                     col--;
58632                 }
58633                 row--;
58634             }
58635         } else {
58636             if(col >= clen){
58637                 row++;
58638                 first = false;
58639             }
58640             while(row < rlen){
58641                 if(!first){
58642                     col = 0;
58643                 }
58644                 first = false;
58645                 while(col < clen){
58646                     if(fn.call(scope || this, row, col, cm) === true){
58647                         return [row, col];
58648                     }
58649                     col++;
58650                 }
58651                 row++;
58652             }
58653         }
58654         return null;
58655     },
58656
58657     // private
58658     getSelections : function(){
58659         return this.selModel.getSelections();
58660     },
58661
58662     /**
58663      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58664      * but if manual update is required this method will initiate it.
58665      */
58666     autoSize : function(){
58667         if(this.rendered){
58668             this.view.layout();
58669             if(this.view.adjustForScroll){
58670                 this.view.adjustForScroll();
58671             }
58672         }
58673     },
58674
58675     /**
58676      * Returns the grid's underlying element.
58677      * @return {Element} The element
58678      */
58679     getGridEl : function(){
58680         return this.container;
58681     },
58682
58683     // private for compatibility, overridden by editor grid
58684     stopEditing : function(){},
58685
58686     /**
58687      * Returns the grid's SelectionModel.
58688      * @return {SelectionModel}
58689      */
58690     getSelectionModel : function(){
58691         if(!this.selModel){
58692             this.selModel = new Roo.grid.RowSelectionModel();
58693         }
58694         return this.selModel;
58695     },
58696
58697     /**
58698      * Returns the grid's DataSource.
58699      * @return {DataSource}
58700      */
58701     getDataSource : function(){
58702         return this.dataSource;
58703     },
58704
58705     /**
58706      * Returns the grid's ColumnModel.
58707      * @return {ColumnModel}
58708      */
58709     getColumnModel : function(){
58710         return this.colModel;
58711     },
58712
58713     /**
58714      * Returns the grid's GridView object.
58715      * @return {GridView}
58716      */
58717     getView : function(){
58718         if(!this.view){
58719             this.view = new Roo.grid.GridView(this.viewConfig);
58720             this.relayEvents(this.view, [
58721                 "beforerowremoved", "beforerowsinserted",
58722                 "beforerefresh", "rowremoved",
58723                 "rowsinserted", "rowupdated" ,"refresh"
58724             ]);
58725         }
58726         return this.view;
58727     },
58728     /**
58729      * Called to get grid's drag proxy text, by default returns this.ddText.
58730      * Override this to put something different in the dragged text.
58731      * @return {String}
58732      */
58733     getDragDropText : function(){
58734         var count = this.selModel.getCount();
58735         return String.format(this.ddText, count, count == 1 ? '' : 's');
58736     }
58737 });
58738 /*
58739  * Based on:
58740  * Ext JS Library 1.1.1
58741  * Copyright(c) 2006-2007, Ext JS, LLC.
58742  *
58743  * Originally Released Under LGPL - original licence link has changed is not relivant.
58744  *
58745  * Fork - LGPL
58746  * <script type="text/javascript">
58747  */
58748  /**
58749  * @class Roo.grid.AbstractGridView
58750  * @extends Roo.util.Observable
58751  * @abstract
58752  * Abstract base class for grid Views
58753  * @constructor
58754  */
58755 Roo.grid.AbstractGridView = function(){
58756         this.grid = null;
58757         
58758         this.events = {
58759             "beforerowremoved" : true,
58760             "beforerowsinserted" : true,
58761             "beforerefresh" : true,
58762             "rowremoved" : true,
58763             "rowsinserted" : true,
58764             "rowupdated" : true,
58765             "refresh" : true
58766         };
58767     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58768 };
58769
58770 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58771     rowClass : "x-grid-row",
58772     cellClass : "x-grid-cell",
58773     tdClass : "x-grid-td",
58774     hdClass : "x-grid-hd",
58775     splitClass : "x-grid-hd-split",
58776     
58777     init: function(grid){
58778         this.grid = grid;
58779                 var cid = this.grid.getGridEl().id;
58780         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58781         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58782         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58783         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58784         },
58785         
58786     getColumnRenderers : function(){
58787         var renderers = [];
58788         var cm = this.grid.colModel;
58789         var colCount = cm.getColumnCount();
58790         for(var i = 0; i < colCount; i++){
58791             renderers[i] = cm.getRenderer(i);
58792         }
58793         return renderers;
58794     },
58795     
58796     getColumnIds : function(){
58797         var ids = [];
58798         var cm = this.grid.colModel;
58799         var colCount = cm.getColumnCount();
58800         for(var i = 0; i < colCount; i++){
58801             ids[i] = cm.getColumnId(i);
58802         }
58803         return ids;
58804     },
58805     
58806     getDataIndexes : function(){
58807         if(!this.indexMap){
58808             this.indexMap = this.buildIndexMap();
58809         }
58810         return this.indexMap.colToData;
58811     },
58812     
58813     getColumnIndexByDataIndex : function(dataIndex){
58814         if(!this.indexMap){
58815             this.indexMap = this.buildIndexMap();
58816         }
58817         return this.indexMap.dataToCol[dataIndex];
58818     },
58819     
58820     /**
58821      * Set a css style for a column dynamically. 
58822      * @param {Number} colIndex The index of the column
58823      * @param {String} name The css property name
58824      * @param {String} value The css value
58825      */
58826     setCSSStyle : function(colIndex, name, value){
58827         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58828         Roo.util.CSS.updateRule(selector, name, value);
58829     },
58830     
58831     generateRules : function(cm){
58832         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58833         Roo.util.CSS.removeStyleSheet(rulesId);
58834         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58835             var cid = cm.getColumnId(i);
58836             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58837                          this.tdSelector, cid, " {\n}\n",
58838                          this.hdSelector, cid, " {\n}\n",
58839                          this.splitSelector, cid, " {\n}\n");
58840         }
58841         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58842     }
58843 });/*
58844  * Based on:
58845  * Ext JS Library 1.1.1
58846  * Copyright(c) 2006-2007, Ext JS, LLC.
58847  *
58848  * Originally Released Under LGPL - original licence link has changed is not relivant.
58849  *
58850  * Fork - LGPL
58851  * <script type="text/javascript">
58852  */
58853
58854 // private
58855 // This is a support class used internally by the Grid components
58856 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58857     this.grid = grid;
58858     this.view = grid.getView();
58859     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58860     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58861     if(hd2){
58862         this.setHandleElId(Roo.id(hd));
58863         this.setOuterHandleElId(Roo.id(hd2));
58864     }
58865     this.scroll = false;
58866 };
58867 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58868     maxDragWidth: 120,
58869     getDragData : function(e){
58870         var t = Roo.lib.Event.getTarget(e);
58871         var h = this.view.findHeaderCell(t);
58872         if(h){
58873             return {ddel: h.firstChild, header:h};
58874         }
58875         return false;
58876     },
58877
58878     onInitDrag : function(e){
58879         this.view.headersDisabled = true;
58880         var clone = this.dragData.ddel.cloneNode(true);
58881         clone.id = Roo.id();
58882         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58883         this.proxy.update(clone);
58884         return true;
58885     },
58886
58887     afterValidDrop : function(){
58888         var v = this.view;
58889         setTimeout(function(){
58890             v.headersDisabled = false;
58891         }, 50);
58892     },
58893
58894     afterInvalidDrop : function(){
58895         var v = this.view;
58896         setTimeout(function(){
58897             v.headersDisabled = false;
58898         }, 50);
58899     }
58900 });
58901 /*
58902  * Based on:
58903  * Ext JS Library 1.1.1
58904  * Copyright(c) 2006-2007, Ext JS, LLC.
58905  *
58906  * Originally Released Under LGPL - original licence link has changed is not relivant.
58907  *
58908  * Fork - LGPL
58909  * <script type="text/javascript">
58910  */
58911 // private
58912 // This is a support class used internally by the Grid components
58913 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58914     this.grid = grid;
58915     this.view = grid.getView();
58916     // split the proxies so they don't interfere with mouse events
58917     this.proxyTop = Roo.DomHelper.append(document.body, {
58918         cls:"col-move-top", html:"&#160;"
58919     }, true);
58920     this.proxyBottom = Roo.DomHelper.append(document.body, {
58921         cls:"col-move-bottom", html:"&#160;"
58922     }, true);
58923     this.proxyTop.hide = this.proxyBottom.hide = function(){
58924         this.setLeftTop(-100,-100);
58925         this.setStyle("visibility", "hidden");
58926     };
58927     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58928     // temporarily disabled
58929     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58930     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58931 };
58932 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58933     proxyOffsets : [-4, -9],
58934     fly: Roo.Element.fly,
58935
58936     getTargetFromEvent : function(e){
58937         var t = Roo.lib.Event.getTarget(e);
58938         var cindex = this.view.findCellIndex(t);
58939         if(cindex !== false){
58940             return this.view.getHeaderCell(cindex);
58941         }
58942         return null;
58943     },
58944
58945     nextVisible : function(h){
58946         var v = this.view, cm = this.grid.colModel;
58947         h = h.nextSibling;
58948         while(h){
58949             if(!cm.isHidden(v.getCellIndex(h))){
58950                 return h;
58951             }
58952             h = h.nextSibling;
58953         }
58954         return null;
58955     },
58956
58957     prevVisible : function(h){
58958         var v = this.view, cm = this.grid.colModel;
58959         h = h.prevSibling;
58960         while(h){
58961             if(!cm.isHidden(v.getCellIndex(h))){
58962                 return h;
58963             }
58964             h = h.prevSibling;
58965         }
58966         return null;
58967     },
58968
58969     positionIndicator : function(h, n, e){
58970         var x = Roo.lib.Event.getPageX(e);
58971         var r = Roo.lib.Dom.getRegion(n.firstChild);
58972         var px, pt, py = r.top + this.proxyOffsets[1];
58973         if((r.right - x) <= (r.right-r.left)/2){
58974             px = r.right+this.view.borderWidth;
58975             pt = "after";
58976         }else{
58977             px = r.left;
58978             pt = "before";
58979         }
58980         var oldIndex = this.view.getCellIndex(h);
58981         var newIndex = this.view.getCellIndex(n);
58982
58983         if(this.grid.colModel.isFixed(newIndex)){
58984             return false;
58985         }
58986
58987         var locked = this.grid.colModel.isLocked(newIndex);
58988
58989         if(pt == "after"){
58990             newIndex++;
58991         }
58992         if(oldIndex < newIndex){
58993             newIndex--;
58994         }
58995         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58996             return false;
58997         }
58998         px +=  this.proxyOffsets[0];
58999         this.proxyTop.setLeftTop(px, py);
59000         this.proxyTop.show();
59001         if(!this.bottomOffset){
59002             this.bottomOffset = this.view.mainHd.getHeight();
59003         }
59004         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59005         this.proxyBottom.show();
59006         return pt;
59007     },
59008
59009     onNodeEnter : function(n, dd, e, data){
59010         if(data.header != n){
59011             this.positionIndicator(data.header, n, e);
59012         }
59013     },
59014
59015     onNodeOver : function(n, dd, e, data){
59016         var result = false;
59017         if(data.header != n){
59018             result = this.positionIndicator(data.header, n, e);
59019         }
59020         if(!result){
59021             this.proxyTop.hide();
59022             this.proxyBottom.hide();
59023         }
59024         return result ? this.dropAllowed : this.dropNotAllowed;
59025     },
59026
59027     onNodeOut : function(n, dd, e, data){
59028         this.proxyTop.hide();
59029         this.proxyBottom.hide();
59030     },
59031
59032     onNodeDrop : function(n, dd, e, data){
59033         var h = data.header;
59034         if(h != n){
59035             var cm = this.grid.colModel;
59036             var x = Roo.lib.Event.getPageX(e);
59037             var r = Roo.lib.Dom.getRegion(n.firstChild);
59038             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59039             var oldIndex = this.view.getCellIndex(h);
59040             var newIndex = this.view.getCellIndex(n);
59041             var locked = cm.isLocked(newIndex);
59042             if(pt == "after"){
59043                 newIndex++;
59044             }
59045             if(oldIndex < newIndex){
59046                 newIndex--;
59047             }
59048             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59049                 return false;
59050             }
59051             cm.setLocked(oldIndex, locked, true);
59052             cm.moveColumn(oldIndex, newIndex);
59053             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59054             return true;
59055         }
59056         return false;
59057     }
59058 });
59059 /*
59060  * Based on:
59061  * Ext JS Library 1.1.1
59062  * Copyright(c) 2006-2007, Ext JS, LLC.
59063  *
59064  * Originally Released Under LGPL - original licence link has changed is not relivant.
59065  *
59066  * Fork - LGPL
59067  * <script type="text/javascript">
59068  */
59069   
59070 /**
59071  * @class Roo.grid.GridView
59072  * @extends Roo.util.Observable
59073  *
59074  * @constructor
59075  * @param {Object} config
59076  */
59077 Roo.grid.GridView = function(config){
59078     Roo.grid.GridView.superclass.constructor.call(this);
59079     this.el = null;
59080
59081     Roo.apply(this, config);
59082 };
59083
59084 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59085
59086     unselectable :  'unselectable="on"',
59087     unselectableCls :  'x-unselectable',
59088     
59089     
59090     rowClass : "x-grid-row",
59091
59092     cellClass : "x-grid-col",
59093
59094     tdClass : "x-grid-td",
59095
59096     hdClass : "x-grid-hd",
59097
59098     splitClass : "x-grid-split",
59099
59100     sortClasses : ["sort-asc", "sort-desc"],
59101
59102     enableMoveAnim : false,
59103
59104     hlColor: "C3DAF9",
59105
59106     dh : Roo.DomHelper,
59107
59108     fly : Roo.Element.fly,
59109
59110     css : Roo.util.CSS,
59111
59112     borderWidth: 1,
59113
59114     splitOffset: 3,
59115
59116     scrollIncrement : 22,
59117
59118     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59119
59120     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59121
59122     bind : function(ds, cm){
59123         if(this.ds){
59124             this.ds.un("load", this.onLoad, this);
59125             this.ds.un("datachanged", this.onDataChange, this);
59126             this.ds.un("add", this.onAdd, this);
59127             this.ds.un("remove", this.onRemove, this);
59128             this.ds.un("update", this.onUpdate, this);
59129             this.ds.un("clear", this.onClear, this);
59130         }
59131         if(ds){
59132             ds.on("load", this.onLoad, this);
59133             ds.on("datachanged", this.onDataChange, this);
59134             ds.on("add", this.onAdd, this);
59135             ds.on("remove", this.onRemove, this);
59136             ds.on("update", this.onUpdate, this);
59137             ds.on("clear", this.onClear, this);
59138         }
59139         this.ds = ds;
59140
59141         if(this.cm){
59142             this.cm.un("widthchange", this.onColWidthChange, this);
59143             this.cm.un("headerchange", this.onHeaderChange, this);
59144             this.cm.un("hiddenchange", this.onHiddenChange, this);
59145             this.cm.un("columnmoved", this.onColumnMove, this);
59146             this.cm.un("columnlockchange", this.onColumnLock, this);
59147         }
59148         if(cm){
59149             this.generateRules(cm);
59150             cm.on("widthchange", this.onColWidthChange, this);
59151             cm.on("headerchange", this.onHeaderChange, this);
59152             cm.on("hiddenchange", this.onHiddenChange, this);
59153             cm.on("columnmoved", this.onColumnMove, this);
59154             cm.on("columnlockchange", this.onColumnLock, this);
59155         }
59156         this.cm = cm;
59157     },
59158
59159     init: function(grid){
59160         Roo.grid.GridView.superclass.init.call(this, grid);
59161
59162         this.bind(grid.dataSource, grid.colModel);
59163
59164         grid.on("headerclick", this.handleHeaderClick, this);
59165
59166         if(grid.trackMouseOver){
59167             grid.on("mouseover", this.onRowOver, this);
59168             grid.on("mouseout", this.onRowOut, this);
59169         }
59170         grid.cancelTextSelection = function(){};
59171         this.gridId = grid.id;
59172
59173         var tpls = this.templates || {};
59174
59175         if(!tpls.master){
59176             tpls.master = new Roo.Template(
59177                '<div class="x-grid" hidefocus="true">',
59178                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59179                   '<div class="x-grid-topbar"></div>',
59180                   '<div class="x-grid-scroller"><div></div></div>',
59181                   '<div class="x-grid-locked">',
59182                       '<div class="x-grid-header">{lockedHeader}</div>',
59183                       '<div class="x-grid-body">{lockedBody}</div>',
59184                   "</div>",
59185                   '<div class="x-grid-viewport">',
59186                       '<div class="x-grid-header">{header}</div>',
59187                       '<div class="x-grid-body">{body}</div>',
59188                   "</div>",
59189                   '<div class="x-grid-bottombar"></div>',
59190                  
59191                   '<div class="x-grid-resize-proxy">&#160;</div>',
59192                "</div>"
59193             );
59194             tpls.master.disableformats = true;
59195         }
59196
59197         if(!tpls.header){
59198             tpls.header = new Roo.Template(
59199                '<table border="0" cellspacing="0" cellpadding="0">',
59200                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59201                "</table>{splits}"
59202             );
59203             tpls.header.disableformats = true;
59204         }
59205         tpls.header.compile();
59206
59207         if(!tpls.hcell){
59208             tpls.hcell = new Roo.Template(
59209                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59210                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59211                 "</div></td>"
59212              );
59213              tpls.hcell.disableFormats = true;
59214         }
59215         tpls.hcell.compile();
59216
59217         if(!tpls.hsplit){
59218             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59219                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59220             tpls.hsplit.disableFormats = true;
59221         }
59222         tpls.hsplit.compile();
59223
59224         if(!tpls.body){
59225             tpls.body = new Roo.Template(
59226                '<table border="0" cellspacing="0" cellpadding="0">',
59227                "<tbody>{rows}</tbody>",
59228                "</table>"
59229             );
59230             tpls.body.disableFormats = true;
59231         }
59232         tpls.body.compile();
59233
59234         if(!tpls.row){
59235             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59236             tpls.row.disableFormats = true;
59237         }
59238         tpls.row.compile();
59239
59240         if(!tpls.cell){
59241             tpls.cell = new Roo.Template(
59242                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59243                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59244                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59245                 "</td>"
59246             );
59247             tpls.cell.disableFormats = true;
59248         }
59249         tpls.cell.compile();
59250
59251         this.templates = tpls;
59252     },
59253
59254     // remap these for backwards compat
59255     onColWidthChange : function(){
59256         this.updateColumns.apply(this, arguments);
59257     },
59258     onHeaderChange : function(){
59259         this.updateHeaders.apply(this, arguments);
59260     }, 
59261     onHiddenChange : function(){
59262         this.handleHiddenChange.apply(this, arguments);
59263     },
59264     onColumnMove : function(){
59265         this.handleColumnMove.apply(this, arguments);
59266     },
59267     onColumnLock : function(){
59268         this.handleLockChange.apply(this, arguments);
59269     },
59270
59271     onDataChange : function(){
59272         this.refresh();
59273         this.updateHeaderSortState();
59274     },
59275
59276     onClear : function(){
59277         this.refresh();
59278     },
59279
59280     onUpdate : function(ds, record){
59281         this.refreshRow(record);
59282     },
59283
59284     refreshRow : function(record){
59285         var ds = this.ds, index;
59286         if(typeof record == 'number'){
59287             index = record;
59288             record = ds.getAt(index);
59289         }else{
59290             index = ds.indexOf(record);
59291         }
59292         this.insertRows(ds, index, index, true);
59293         this.onRemove(ds, record, index+1, true);
59294         this.syncRowHeights(index, index);
59295         this.layout();
59296         this.fireEvent("rowupdated", this, index, record);
59297     },
59298
59299     onAdd : function(ds, records, index){
59300         this.insertRows(ds, index, index + (records.length-1));
59301     },
59302
59303     onRemove : function(ds, record, index, isUpdate){
59304         if(isUpdate !== true){
59305             this.fireEvent("beforerowremoved", this, index, record);
59306         }
59307         var bt = this.getBodyTable(), lt = this.getLockedTable();
59308         if(bt.rows[index]){
59309             bt.firstChild.removeChild(bt.rows[index]);
59310         }
59311         if(lt.rows[index]){
59312             lt.firstChild.removeChild(lt.rows[index]);
59313         }
59314         if(isUpdate !== true){
59315             this.stripeRows(index);
59316             this.syncRowHeights(index, index);
59317             this.layout();
59318             this.fireEvent("rowremoved", this, index, record);
59319         }
59320     },
59321
59322     onLoad : function(){
59323         this.scrollToTop();
59324     },
59325
59326     /**
59327      * Scrolls the grid to the top
59328      */
59329     scrollToTop : function(){
59330         if(this.scroller){
59331             this.scroller.dom.scrollTop = 0;
59332             this.syncScroll();
59333         }
59334     },
59335
59336     /**
59337      * Gets a panel in the header of the grid that can be used for toolbars etc.
59338      * After modifying the contents of this panel a call to grid.autoSize() may be
59339      * required to register any changes in size.
59340      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59341      * @return Roo.Element
59342      */
59343     getHeaderPanel : function(doShow){
59344         if(doShow){
59345             this.headerPanel.show();
59346         }
59347         return this.headerPanel;
59348     },
59349
59350     /**
59351      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59352      * After modifying the contents of this panel a call to grid.autoSize() may be
59353      * required to register any changes in size.
59354      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59355      * @return Roo.Element
59356      */
59357     getFooterPanel : function(doShow){
59358         if(doShow){
59359             this.footerPanel.show();
59360         }
59361         return this.footerPanel;
59362     },
59363
59364     initElements : function(){
59365         var E = Roo.Element;
59366         var el = this.grid.getGridEl().dom.firstChild;
59367         var cs = el.childNodes;
59368
59369         this.el = new E(el);
59370         
59371          this.focusEl = new E(el.firstChild);
59372         this.focusEl.swallowEvent("click", true);
59373         
59374         this.headerPanel = new E(cs[1]);
59375         this.headerPanel.enableDisplayMode("block");
59376
59377         this.scroller = new E(cs[2]);
59378         this.scrollSizer = new E(this.scroller.dom.firstChild);
59379
59380         this.lockedWrap = new E(cs[3]);
59381         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59382         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59383
59384         this.mainWrap = new E(cs[4]);
59385         this.mainHd = new E(this.mainWrap.dom.firstChild);
59386         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59387
59388         this.footerPanel = new E(cs[5]);
59389         this.footerPanel.enableDisplayMode("block");
59390
59391         this.resizeProxy = new E(cs[6]);
59392
59393         this.headerSelector = String.format(
59394            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59395            this.lockedHd.id, this.mainHd.id
59396         );
59397
59398         this.splitterSelector = String.format(
59399            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59400            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59401         );
59402     },
59403     idToCssName : function(s)
59404     {
59405         return s.replace(/[^a-z0-9]+/ig, '-');
59406     },
59407
59408     getHeaderCell : function(index){
59409         return Roo.DomQuery.select(this.headerSelector)[index];
59410     },
59411
59412     getHeaderCellMeasure : function(index){
59413         return this.getHeaderCell(index).firstChild;
59414     },
59415
59416     getHeaderCellText : function(index){
59417         return this.getHeaderCell(index).firstChild.firstChild;
59418     },
59419
59420     getLockedTable : function(){
59421         return this.lockedBody.dom.firstChild;
59422     },
59423
59424     getBodyTable : function(){
59425         return this.mainBody.dom.firstChild;
59426     },
59427
59428     getLockedRow : function(index){
59429         return this.getLockedTable().rows[index];
59430     },
59431
59432     getRow : function(index){
59433         return this.getBodyTable().rows[index];
59434     },
59435
59436     getRowComposite : function(index){
59437         if(!this.rowEl){
59438             this.rowEl = new Roo.CompositeElementLite();
59439         }
59440         var els = [], lrow, mrow;
59441         if(lrow = this.getLockedRow(index)){
59442             els.push(lrow);
59443         }
59444         if(mrow = this.getRow(index)){
59445             els.push(mrow);
59446         }
59447         this.rowEl.elements = els;
59448         return this.rowEl;
59449     },
59450     /**
59451      * Gets the 'td' of the cell
59452      * 
59453      * @param {Integer} rowIndex row to select
59454      * @param {Integer} colIndex column to select
59455      * 
59456      * @return {Object} 
59457      */
59458     getCell : function(rowIndex, colIndex){
59459         var locked = this.cm.getLockedCount();
59460         var source;
59461         if(colIndex < locked){
59462             source = this.lockedBody.dom.firstChild;
59463         }else{
59464             source = this.mainBody.dom.firstChild;
59465             colIndex -= locked;
59466         }
59467         return source.rows[rowIndex].childNodes[colIndex];
59468     },
59469
59470     getCellText : function(rowIndex, colIndex){
59471         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59472     },
59473
59474     getCellBox : function(cell){
59475         var b = this.fly(cell).getBox();
59476         if(Roo.isOpera){ // opera fails to report the Y
59477             b.y = cell.offsetTop + this.mainBody.getY();
59478         }
59479         return b;
59480     },
59481
59482     getCellIndex : function(cell){
59483         var id = String(cell.className).match(this.cellRE);
59484         if(id){
59485             return parseInt(id[1], 10);
59486         }
59487         return 0;
59488     },
59489
59490     findHeaderIndex : function(n){
59491         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59492         return r ? this.getCellIndex(r) : false;
59493     },
59494
59495     findHeaderCell : function(n){
59496         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59497         return r ? r : false;
59498     },
59499
59500     findRowIndex : function(n){
59501         if(!n){
59502             return false;
59503         }
59504         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59505         return r ? r.rowIndex : false;
59506     },
59507
59508     findCellIndex : function(node){
59509         var stop = this.el.dom;
59510         while(node && node != stop){
59511             if(this.findRE.test(node.className)){
59512                 return this.getCellIndex(node);
59513             }
59514             node = node.parentNode;
59515         }
59516         return false;
59517     },
59518
59519     getColumnId : function(index){
59520         return this.cm.getColumnId(index);
59521     },
59522
59523     getSplitters : function()
59524     {
59525         if(this.splitterSelector){
59526            return Roo.DomQuery.select(this.splitterSelector);
59527         }else{
59528             return null;
59529       }
59530     },
59531
59532     getSplitter : function(index){
59533         return this.getSplitters()[index];
59534     },
59535
59536     onRowOver : function(e, t){
59537         var row;
59538         if((row = this.findRowIndex(t)) !== false){
59539             this.getRowComposite(row).addClass("x-grid-row-over");
59540         }
59541     },
59542
59543     onRowOut : function(e, t){
59544         var row;
59545         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59546             this.getRowComposite(row).removeClass("x-grid-row-over");
59547         }
59548     },
59549
59550     renderHeaders : function(){
59551         var cm = this.cm;
59552         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59553         var cb = [], lb = [], sb = [], lsb = [], p = {};
59554         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59555             p.cellId = "x-grid-hd-0-" + i;
59556             p.splitId = "x-grid-csplit-0-" + i;
59557             p.id = cm.getColumnId(i);
59558             p.value = cm.getColumnHeader(i) || "";
59559             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59560             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59561             if(!cm.isLocked(i)){
59562                 cb[cb.length] = ct.apply(p);
59563                 sb[sb.length] = st.apply(p);
59564             }else{
59565                 lb[lb.length] = ct.apply(p);
59566                 lsb[lsb.length] = st.apply(p);
59567             }
59568         }
59569         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59570                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59571     },
59572
59573     updateHeaders : function(){
59574         var html = this.renderHeaders();
59575         this.lockedHd.update(html[0]);
59576         this.mainHd.update(html[1]);
59577     },
59578
59579     /**
59580      * Focuses the specified row.
59581      * @param {Number} row The row index
59582      */
59583     focusRow : function(row)
59584     {
59585         //Roo.log('GridView.focusRow');
59586         var x = this.scroller.dom.scrollLeft;
59587         this.focusCell(row, 0, false);
59588         this.scroller.dom.scrollLeft = x;
59589     },
59590
59591     /**
59592      * Focuses the specified cell.
59593      * @param {Number} row The row index
59594      * @param {Number} col The column index
59595      * @param {Boolean} hscroll false to disable horizontal scrolling
59596      */
59597     focusCell : function(row, col, hscroll)
59598     {
59599         //Roo.log('GridView.focusCell');
59600         var el = this.ensureVisible(row, col, hscroll);
59601         this.focusEl.alignTo(el, "tl-tl");
59602         if(Roo.isGecko){
59603             this.focusEl.focus();
59604         }else{
59605             this.focusEl.focus.defer(1, this.focusEl);
59606         }
59607     },
59608
59609     /**
59610      * Scrolls the specified cell into view
59611      * @param {Number} row The row index
59612      * @param {Number} col The column index
59613      * @param {Boolean} hscroll false to disable horizontal scrolling
59614      */
59615     ensureVisible : function(row, col, hscroll)
59616     {
59617         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59618         //return null; //disable for testing.
59619         if(typeof row != "number"){
59620             row = row.rowIndex;
59621         }
59622         if(row < 0 && row >= this.ds.getCount()){
59623             return  null;
59624         }
59625         col = (col !== undefined ? col : 0);
59626         var cm = this.grid.colModel;
59627         while(cm.isHidden(col)){
59628             col++;
59629         }
59630
59631         var el = this.getCell(row, col);
59632         if(!el){
59633             return null;
59634         }
59635         var c = this.scroller.dom;
59636
59637         var ctop = parseInt(el.offsetTop, 10);
59638         var cleft = parseInt(el.offsetLeft, 10);
59639         var cbot = ctop + el.offsetHeight;
59640         var cright = cleft + el.offsetWidth;
59641         
59642         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59643         var stop = parseInt(c.scrollTop, 10);
59644         var sleft = parseInt(c.scrollLeft, 10);
59645         var sbot = stop + ch;
59646         var sright = sleft + c.clientWidth;
59647         /*
59648         Roo.log('GridView.ensureVisible:' +
59649                 ' ctop:' + ctop +
59650                 ' c.clientHeight:' + c.clientHeight +
59651                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59652                 ' stop:' + stop +
59653                 ' cbot:' + cbot +
59654                 ' sbot:' + sbot +
59655                 ' ch:' + ch  
59656                 );
59657         */
59658         if(ctop < stop){
59659             c.scrollTop = ctop;
59660             //Roo.log("set scrolltop to ctop DISABLE?");
59661         }else if(cbot > sbot){
59662             //Roo.log("set scrolltop to cbot-ch");
59663             c.scrollTop = cbot-ch;
59664         }
59665         
59666         if(hscroll !== false){
59667             if(cleft < sleft){
59668                 c.scrollLeft = cleft;
59669             }else if(cright > sright){
59670                 c.scrollLeft = cright-c.clientWidth;
59671             }
59672         }
59673          
59674         return el;
59675     },
59676
59677     updateColumns : function(){
59678         this.grid.stopEditing();
59679         var cm = this.grid.colModel, colIds = this.getColumnIds();
59680         //var totalWidth = cm.getTotalWidth();
59681         var pos = 0;
59682         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59683             //if(cm.isHidden(i)) continue;
59684             var w = cm.getColumnWidth(i);
59685             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59686             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59687         }
59688         this.updateSplitters();
59689     },
59690
59691     generateRules : function(cm){
59692         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59693         Roo.util.CSS.removeStyleSheet(rulesId);
59694         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59695             var cid = cm.getColumnId(i);
59696             var align = '';
59697             if(cm.config[i].align){
59698                 align = 'text-align:'+cm.config[i].align+';';
59699             }
59700             var hidden = '';
59701             if(cm.isHidden(i)){
59702                 hidden = 'display:none;';
59703             }
59704             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59705             ruleBuf.push(
59706                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59707                     this.hdSelector, cid, " {\n", align, width, "}\n",
59708                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59709                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59710         }
59711         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59712     },
59713
59714     updateSplitters : function(){
59715         var cm = this.cm, s = this.getSplitters();
59716         if(s){ // splitters not created yet
59717             var pos = 0, locked = true;
59718             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59719                 if(cm.isHidden(i)) {
59720                     continue;
59721                 }
59722                 var w = cm.getColumnWidth(i); // make sure it's a number
59723                 if(!cm.isLocked(i) && locked){
59724                     pos = 0;
59725                     locked = false;
59726                 }
59727                 pos += w;
59728                 s[i].style.left = (pos-this.splitOffset) + "px";
59729             }
59730         }
59731     },
59732
59733     handleHiddenChange : function(colModel, colIndex, hidden){
59734         if(hidden){
59735             this.hideColumn(colIndex);
59736         }else{
59737             this.unhideColumn(colIndex);
59738         }
59739     },
59740
59741     hideColumn : function(colIndex){
59742         var cid = this.getColumnId(colIndex);
59743         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59744         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59745         if(Roo.isSafari){
59746             this.updateHeaders();
59747         }
59748         this.updateSplitters();
59749         this.layout();
59750     },
59751
59752     unhideColumn : function(colIndex){
59753         var cid = this.getColumnId(colIndex);
59754         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59755         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59756
59757         if(Roo.isSafari){
59758             this.updateHeaders();
59759         }
59760         this.updateSplitters();
59761         this.layout();
59762     },
59763
59764     insertRows : function(dm, firstRow, lastRow, isUpdate){
59765         if(firstRow == 0 && lastRow == dm.getCount()-1){
59766             this.refresh();
59767         }else{
59768             if(!isUpdate){
59769                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59770             }
59771             var s = this.getScrollState();
59772             var markup = this.renderRows(firstRow, lastRow);
59773             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59774             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59775             this.restoreScroll(s);
59776             if(!isUpdate){
59777                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59778                 this.syncRowHeights(firstRow, lastRow);
59779                 this.stripeRows(firstRow);
59780                 this.layout();
59781             }
59782         }
59783     },
59784
59785     bufferRows : function(markup, target, index){
59786         var before = null, trows = target.rows, tbody = target.tBodies[0];
59787         if(index < trows.length){
59788             before = trows[index];
59789         }
59790         var b = document.createElement("div");
59791         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59792         var rows = b.firstChild.rows;
59793         for(var i = 0, len = rows.length; i < len; i++){
59794             if(before){
59795                 tbody.insertBefore(rows[0], before);
59796             }else{
59797                 tbody.appendChild(rows[0]);
59798             }
59799         }
59800         b.innerHTML = "";
59801         b = null;
59802     },
59803
59804     deleteRows : function(dm, firstRow, lastRow){
59805         if(dm.getRowCount()<1){
59806             this.fireEvent("beforerefresh", this);
59807             this.mainBody.update("");
59808             this.lockedBody.update("");
59809             this.fireEvent("refresh", this);
59810         }else{
59811             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59812             var bt = this.getBodyTable();
59813             var tbody = bt.firstChild;
59814             var rows = bt.rows;
59815             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59816                 tbody.removeChild(rows[firstRow]);
59817             }
59818             this.stripeRows(firstRow);
59819             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59820         }
59821     },
59822
59823     updateRows : function(dataSource, firstRow, lastRow){
59824         var s = this.getScrollState();
59825         this.refresh();
59826         this.restoreScroll(s);
59827     },
59828
59829     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59830         if(!noRefresh){
59831            this.refresh();
59832         }
59833         this.updateHeaderSortState();
59834     },
59835
59836     getScrollState : function(){
59837         
59838         var sb = this.scroller.dom;
59839         return {left: sb.scrollLeft, top: sb.scrollTop};
59840     },
59841
59842     stripeRows : function(startRow){
59843         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59844             return;
59845         }
59846         startRow = startRow || 0;
59847         var rows = this.getBodyTable().rows;
59848         var lrows = this.getLockedTable().rows;
59849         var cls = ' x-grid-row-alt ';
59850         for(var i = startRow, len = rows.length; i < len; i++){
59851             var row = rows[i], lrow = lrows[i];
59852             var isAlt = ((i+1) % 2 == 0);
59853             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59854             if(isAlt == hasAlt){
59855                 continue;
59856             }
59857             if(isAlt){
59858                 row.className += " x-grid-row-alt";
59859             }else{
59860                 row.className = row.className.replace("x-grid-row-alt", "");
59861             }
59862             if(lrow){
59863                 lrow.className = row.className;
59864             }
59865         }
59866     },
59867
59868     restoreScroll : function(state){
59869         //Roo.log('GridView.restoreScroll');
59870         var sb = this.scroller.dom;
59871         sb.scrollLeft = state.left;
59872         sb.scrollTop = state.top;
59873         this.syncScroll();
59874     },
59875
59876     syncScroll : function(){
59877         //Roo.log('GridView.syncScroll');
59878         var sb = this.scroller.dom;
59879         var sh = this.mainHd.dom;
59880         var bs = this.mainBody.dom;
59881         var lv = this.lockedBody.dom;
59882         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59883         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59884     },
59885
59886     handleScroll : function(e){
59887         this.syncScroll();
59888         var sb = this.scroller.dom;
59889         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59890         e.stopEvent();
59891     },
59892
59893     handleWheel : function(e){
59894         var d = e.getWheelDelta();
59895         this.scroller.dom.scrollTop -= d*22;
59896         // set this here to prevent jumpy scrolling on large tables
59897         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59898         e.stopEvent();
59899     },
59900
59901     renderRows : function(startRow, endRow){
59902         // pull in all the crap needed to render rows
59903         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59904         var colCount = cm.getColumnCount();
59905
59906         if(ds.getCount() < 1){
59907             return ["", ""];
59908         }
59909
59910         // build a map for all the columns
59911         var cs = [];
59912         for(var i = 0; i < colCount; i++){
59913             var name = cm.getDataIndex(i);
59914             cs[i] = {
59915                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59916                 renderer : cm.getRenderer(i),
59917                 id : cm.getColumnId(i),
59918                 locked : cm.isLocked(i),
59919                 has_editor : cm.isCellEditable(i)
59920             };
59921         }
59922
59923         startRow = startRow || 0;
59924         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59925
59926         // records to render
59927         var rs = ds.getRange(startRow, endRow);
59928
59929         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59930     },
59931
59932     // As much as I hate to duplicate code, this was branched because FireFox really hates
59933     // [].join("") on strings. The performance difference was substantial enough to
59934     // branch this function
59935     doRender : Roo.isGecko ?
59936             function(cs, rs, ds, startRow, colCount, stripe){
59937                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59938                 // buffers
59939                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59940                 
59941                 var hasListener = this.grid.hasListener('rowclass');
59942                 var rowcfg = {};
59943                 for(var j = 0, len = rs.length; j < len; j++){
59944                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59945                     for(var i = 0; i < colCount; i++){
59946                         c = cs[i];
59947                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59948                         p.id = c.id;
59949                         p.css = p.attr = "";
59950                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59951                         if(p.value == undefined || p.value === "") {
59952                             p.value = "&#160;";
59953                         }
59954                         if(c.has_editor){
59955                             p.css += ' x-grid-editable-cell';
59956                         }
59957                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59958                             p.css +=  ' x-grid-dirty-cell';
59959                         }
59960                         var markup = ct.apply(p);
59961                         if(!c.locked){
59962                             cb+= markup;
59963                         }else{
59964                             lcb+= markup;
59965                         }
59966                     }
59967                     var alt = [];
59968                     if(stripe && ((rowIndex+1) % 2 == 0)){
59969                         alt.push("x-grid-row-alt")
59970                     }
59971                     if(r.dirty){
59972                         alt.push(  " x-grid-dirty-row");
59973                     }
59974                     rp.cells = lcb;
59975                     if(this.getRowClass){
59976                         alt.push(this.getRowClass(r, rowIndex));
59977                     }
59978                     if (hasListener) {
59979                         rowcfg = {
59980                              
59981                             record: r,
59982                             rowIndex : rowIndex,
59983                             rowClass : ''
59984                         };
59985                         this.grid.fireEvent('rowclass', this, rowcfg);
59986                         alt.push(rowcfg.rowClass);
59987                     }
59988                     rp.alt = alt.join(" ");
59989                     lbuf+= rt.apply(rp);
59990                     rp.cells = cb;
59991                     buf+=  rt.apply(rp);
59992                 }
59993                 return [lbuf, buf];
59994             } :
59995             function(cs, rs, ds, startRow, colCount, stripe){
59996                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59997                 // buffers
59998                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59999                 var hasListener = this.grid.hasListener('rowclass');
60000  
60001                 var rowcfg = {};
60002                 for(var j = 0, len = rs.length; j < len; j++){
60003                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60004                     for(var i = 0; i < colCount; i++){
60005                         c = cs[i];
60006                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60007                         p.id = c.id;
60008                         p.css = p.attr = "";
60009                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60010                         if(p.value == undefined || p.value === "") {
60011                             p.value = "&#160;";
60012                         }
60013                         //Roo.log(c);
60014                          if(c.has_editor){
60015                             p.css += ' x-grid-editable-cell';
60016                         }
60017                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60018                             p.css += ' x-grid-dirty-cell' 
60019                         }
60020                         
60021                         var markup = ct.apply(p);
60022                         if(!c.locked){
60023                             cb[cb.length] = markup;
60024                         }else{
60025                             lcb[lcb.length] = markup;
60026                         }
60027                     }
60028                     var alt = [];
60029                     if(stripe && ((rowIndex+1) % 2 == 0)){
60030                         alt.push( "x-grid-row-alt");
60031                     }
60032                     if(r.dirty){
60033                         alt.push(" x-grid-dirty-row");
60034                     }
60035                     rp.cells = lcb;
60036                     if(this.getRowClass){
60037                         alt.push( this.getRowClass(r, rowIndex));
60038                     }
60039                     if (hasListener) {
60040                         rowcfg = {
60041                              
60042                             record: r,
60043                             rowIndex : rowIndex,
60044                             rowClass : ''
60045                         };
60046                         this.grid.fireEvent('rowclass', this, rowcfg);
60047                         alt.push(rowcfg.rowClass);
60048                     }
60049                     
60050                     rp.alt = alt.join(" ");
60051                     rp.cells = lcb.join("");
60052                     lbuf[lbuf.length] = rt.apply(rp);
60053                     rp.cells = cb.join("");
60054                     buf[buf.length] =  rt.apply(rp);
60055                 }
60056                 return [lbuf.join(""), buf.join("")];
60057             },
60058
60059     renderBody : function(){
60060         var markup = this.renderRows();
60061         var bt = this.templates.body;
60062         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60063     },
60064
60065     /**
60066      * Refreshes the grid
60067      * @param {Boolean} headersToo
60068      */
60069     refresh : function(headersToo){
60070         this.fireEvent("beforerefresh", this);
60071         this.grid.stopEditing();
60072         var result = this.renderBody();
60073         this.lockedBody.update(result[0]);
60074         this.mainBody.update(result[1]);
60075         if(headersToo === true){
60076             this.updateHeaders();
60077             this.updateColumns();
60078             this.updateSplitters();
60079             this.updateHeaderSortState();
60080         }
60081         this.syncRowHeights();
60082         this.layout();
60083         this.fireEvent("refresh", this);
60084     },
60085
60086     handleColumnMove : function(cm, oldIndex, newIndex){
60087         this.indexMap = null;
60088         var s = this.getScrollState();
60089         this.refresh(true);
60090         this.restoreScroll(s);
60091         this.afterMove(newIndex);
60092     },
60093
60094     afterMove : function(colIndex){
60095         if(this.enableMoveAnim && Roo.enableFx){
60096             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60097         }
60098         // if multisort - fix sortOrder, and reload..
60099         if (this.grid.dataSource.multiSort) {
60100             // the we can call sort again..
60101             var dm = this.grid.dataSource;
60102             var cm = this.grid.colModel;
60103             var so = [];
60104             for(var i = 0; i < cm.config.length; i++ ) {
60105                 
60106                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60107                     continue; // dont' bother, it's not in sort list or being set.
60108                 }
60109                 
60110                 so.push(cm.config[i].dataIndex);
60111             };
60112             dm.sortOrder = so;
60113             dm.load(dm.lastOptions);
60114             
60115             
60116         }
60117         
60118     },
60119
60120     updateCell : function(dm, rowIndex, dataIndex){
60121         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60122         if(typeof colIndex == "undefined"){ // not present in grid
60123             return;
60124         }
60125         var cm = this.grid.colModel;
60126         var cell = this.getCell(rowIndex, colIndex);
60127         var cellText = this.getCellText(rowIndex, colIndex);
60128
60129         var p = {
60130             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60131             id : cm.getColumnId(colIndex),
60132             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60133         };
60134         var renderer = cm.getRenderer(colIndex);
60135         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60136         if(typeof val == "undefined" || val === "") {
60137             val = "&#160;";
60138         }
60139         cellText.innerHTML = val;
60140         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60141         this.syncRowHeights(rowIndex, rowIndex);
60142     },
60143
60144     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60145         var maxWidth = 0;
60146         if(this.grid.autoSizeHeaders){
60147             var h = this.getHeaderCellMeasure(colIndex);
60148             maxWidth = Math.max(maxWidth, h.scrollWidth);
60149         }
60150         var tb, index;
60151         if(this.cm.isLocked(colIndex)){
60152             tb = this.getLockedTable();
60153             index = colIndex;
60154         }else{
60155             tb = this.getBodyTable();
60156             index = colIndex - this.cm.getLockedCount();
60157         }
60158         if(tb && tb.rows){
60159             var rows = tb.rows;
60160             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60161             for(var i = 0; i < stopIndex; i++){
60162                 var cell = rows[i].childNodes[index].firstChild;
60163                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60164             }
60165         }
60166         return maxWidth + /*margin for error in IE*/ 5;
60167     },
60168     /**
60169      * Autofit a column to its content.
60170      * @param {Number} colIndex
60171      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60172      */
60173      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60174          if(this.cm.isHidden(colIndex)){
60175              return; // can't calc a hidden column
60176          }
60177         if(forceMinSize){
60178             var cid = this.cm.getColumnId(colIndex);
60179             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60180            if(this.grid.autoSizeHeaders){
60181                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60182            }
60183         }
60184         var newWidth = this.calcColumnWidth(colIndex);
60185         this.cm.setColumnWidth(colIndex,
60186             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60187         if(!suppressEvent){
60188             this.grid.fireEvent("columnresize", colIndex, newWidth);
60189         }
60190     },
60191
60192     /**
60193      * Autofits all columns to their content and then expands to fit any extra space in the grid
60194      */
60195      autoSizeColumns : function(){
60196         var cm = this.grid.colModel;
60197         var colCount = cm.getColumnCount();
60198         for(var i = 0; i < colCount; i++){
60199             this.autoSizeColumn(i, true, true);
60200         }
60201         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60202             this.fitColumns();
60203         }else{
60204             this.updateColumns();
60205             this.layout();
60206         }
60207     },
60208
60209     /**
60210      * Autofits all columns to the grid's width proportionate with their current size
60211      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60212      */
60213     fitColumns : function(reserveScrollSpace){
60214         var cm = this.grid.colModel;
60215         var colCount = cm.getColumnCount();
60216         var cols = [];
60217         var width = 0;
60218         var i, w;
60219         for (i = 0; i < colCount; i++){
60220             if(!cm.isHidden(i) && !cm.isFixed(i)){
60221                 w = cm.getColumnWidth(i);
60222                 cols.push(i);
60223                 cols.push(w);
60224                 width += w;
60225             }
60226         }
60227         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60228         if(reserveScrollSpace){
60229             avail -= 17;
60230         }
60231         var frac = (avail - cm.getTotalWidth())/width;
60232         while (cols.length){
60233             w = cols.pop();
60234             i = cols.pop();
60235             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60236         }
60237         this.updateColumns();
60238         this.layout();
60239     },
60240
60241     onRowSelect : function(rowIndex){
60242         var row = this.getRowComposite(rowIndex);
60243         row.addClass("x-grid-row-selected");
60244     },
60245
60246     onRowDeselect : function(rowIndex){
60247         var row = this.getRowComposite(rowIndex);
60248         row.removeClass("x-grid-row-selected");
60249     },
60250
60251     onCellSelect : function(row, col){
60252         var cell = this.getCell(row, col);
60253         if(cell){
60254             Roo.fly(cell).addClass("x-grid-cell-selected");
60255         }
60256     },
60257
60258     onCellDeselect : function(row, col){
60259         var cell = this.getCell(row, col);
60260         if(cell){
60261             Roo.fly(cell).removeClass("x-grid-cell-selected");
60262         }
60263     },
60264
60265     updateHeaderSortState : function(){
60266         
60267         // sort state can be single { field: xxx, direction : yyy}
60268         // or   { xxx=>ASC , yyy : DESC ..... }
60269         
60270         var mstate = {};
60271         if (!this.ds.multiSort) { 
60272             var state = this.ds.getSortState();
60273             if(!state){
60274                 return;
60275             }
60276             mstate[state.field] = state.direction;
60277             // FIXME... - this is not used here.. but might be elsewhere..
60278             this.sortState = state;
60279             
60280         } else {
60281             mstate = this.ds.sortToggle;
60282         }
60283         //remove existing sort classes..
60284         
60285         var sc = this.sortClasses;
60286         var hds = this.el.select(this.headerSelector).removeClass(sc);
60287         
60288         for(var f in mstate) {
60289         
60290             var sortColumn = this.cm.findColumnIndex(f);
60291             
60292             if(sortColumn != -1){
60293                 var sortDir = mstate[f];        
60294                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60295             }
60296         }
60297         
60298          
60299         
60300     },
60301
60302
60303     handleHeaderClick : function(g, index,e){
60304         
60305         Roo.log("header click");
60306         
60307         if (Roo.isTouch) {
60308             // touch events on header are handled by context
60309             this.handleHdCtx(g,index,e);
60310             return;
60311         }
60312         
60313         
60314         if(this.headersDisabled){
60315             return;
60316         }
60317         var dm = g.dataSource, cm = g.colModel;
60318         if(!cm.isSortable(index)){
60319             return;
60320         }
60321         g.stopEditing();
60322         
60323         if (dm.multiSort) {
60324             // update the sortOrder
60325             var so = [];
60326             for(var i = 0; i < cm.config.length; i++ ) {
60327                 
60328                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60329                     continue; // dont' bother, it's not in sort list or being set.
60330                 }
60331                 
60332                 so.push(cm.config[i].dataIndex);
60333             };
60334             dm.sortOrder = so;
60335         }
60336         
60337         
60338         dm.sort(cm.getDataIndex(index));
60339     },
60340
60341
60342     destroy : function(){
60343         if(this.colMenu){
60344             this.colMenu.removeAll();
60345             Roo.menu.MenuMgr.unregister(this.colMenu);
60346             this.colMenu.getEl().remove();
60347             delete this.colMenu;
60348         }
60349         if(this.hmenu){
60350             this.hmenu.removeAll();
60351             Roo.menu.MenuMgr.unregister(this.hmenu);
60352             this.hmenu.getEl().remove();
60353             delete this.hmenu;
60354         }
60355         if(this.grid.enableColumnMove){
60356             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60357             if(dds){
60358                 for(var dd in dds){
60359                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60360                         var elid = dds[dd].dragElId;
60361                         dds[dd].unreg();
60362                         Roo.get(elid).remove();
60363                     } else if(dds[dd].config.isTarget){
60364                         dds[dd].proxyTop.remove();
60365                         dds[dd].proxyBottom.remove();
60366                         dds[dd].unreg();
60367                     }
60368                     if(Roo.dd.DDM.locationCache[dd]){
60369                         delete Roo.dd.DDM.locationCache[dd];
60370                     }
60371                 }
60372                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60373             }
60374         }
60375         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60376         this.bind(null, null);
60377         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60378     },
60379
60380     handleLockChange : function(){
60381         this.refresh(true);
60382     },
60383
60384     onDenyColumnLock : function(){
60385
60386     },
60387
60388     onDenyColumnHide : function(){
60389
60390     },
60391
60392     handleHdMenuClick : function(item){
60393         var index = this.hdCtxIndex;
60394         var cm = this.cm, ds = this.ds;
60395         switch(item.id){
60396             case "asc":
60397                 ds.sort(cm.getDataIndex(index), "ASC");
60398                 break;
60399             case "desc":
60400                 ds.sort(cm.getDataIndex(index), "DESC");
60401                 break;
60402             case "lock":
60403                 var lc = cm.getLockedCount();
60404                 if(cm.getColumnCount(true) <= lc+1){
60405                     this.onDenyColumnLock();
60406                     return;
60407                 }
60408                 if(lc != index){
60409                     cm.setLocked(index, true, true);
60410                     cm.moveColumn(index, lc);
60411                     this.grid.fireEvent("columnmove", index, lc);
60412                 }else{
60413                     cm.setLocked(index, true);
60414                 }
60415             break;
60416             case "unlock":
60417                 var lc = cm.getLockedCount();
60418                 if((lc-1) != index){
60419                     cm.setLocked(index, false, true);
60420                     cm.moveColumn(index, lc-1);
60421                     this.grid.fireEvent("columnmove", index, lc-1);
60422                 }else{
60423                     cm.setLocked(index, false);
60424                 }
60425             break;
60426             case 'wider': // used to expand cols on touch..
60427             case 'narrow':
60428                 var cw = cm.getColumnWidth(index);
60429                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60430                 cw = Math.max(0, cw);
60431                 cw = Math.min(cw,4000);
60432                 cm.setColumnWidth(index, cw);
60433                 break;
60434                 
60435             default:
60436                 index = cm.getIndexById(item.id.substr(4));
60437                 if(index != -1){
60438                     if(item.checked && cm.getColumnCount(true) <= 1){
60439                         this.onDenyColumnHide();
60440                         return false;
60441                     }
60442                     cm.setHidden(index, item.checked);
60443                 }
60444         }
60445         return true;
60446     },
60447
60448     beforeColMenuShow : function(){
60449         var cm = this.cm,  colCount = cm.getColumnCount();
60450         this.colMenu.removeAll();
60451         
60452         var items = [];
60453         for(var i = 0; i < colCount; i++){
60454             items.push({
60455                 id: "col-"+cm.getColumnId(i),
60456                 text: cm.getColumnHeader(i),
60457                 checked: !cm.isHidden(i),
60458                 hideOnClick:false
60459             });
60460         }
60461         
60462         if (this.grid.sortColMenu) {
60463             items.sort(function(a,b) {
60464                 if (a.text == b.text) {
60465                     return 0;
60466                 }
60467                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60468             });
60469         }
60470         
60471         for(var i = 0; i < colCount; i++){
60472             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60473         }
60474     },
60475
60476     handleHdCtx : function(g, index, e){
60477         e.stopEvent();
60478         var hd = this.getHeaderCell(index);
60479         this.hdCtxIndex = index;
60480         var ms = this.hmenu.items, cm = this.cm;
60481         ms.get("asc").setDisabled(!cm.isSortable(index));
60482         ms.get("desc").setDisabled(!cm.isSortable(index));
60483         if(this.grid.enableColLock !== false){
60484             ms.get("lock").setDisabled(cm.isLocked(index));
60485             ms.get("unlock").setDisabled(!cm.isLocked(index));
60486         }
60487         this.hmenu.show(hd, "tl-bl");
60488     },
60489
60490     handleHdOver : function(e){
60491         var hd = this.findHeaderCell(e.getTarget());
60492         if(hd && !this.headersDisabled){
60493             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60494                this.fly(hd).addClass("x-grid-hd-over");
60495             }
60496         }
60497     },
60498
60499     handleHdOut : function(e){
60500         var hd = this.findHeaderCell(e.getTarget());
60501         if(hd){
60502             this.fly(hd).removeClass("x-grid-hd-over");
60503         }
60504     },
60505
60506     handleSplitDblClick : function(e, t){
60507         var i = this.getCellIndex(t);
60508         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60509             this.autoSizeColumn(i, true);
60510             this.layout();
60511         }
60512     },
60513
60514     render : function(){
60515
60516         var cm = this.cm;
60517         var colCount = cm.getColumnCount();
60518
60519         if(this.grid.monitorWindowResize === true){
60520             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60521         }
60522         var header = this.renderHeaders();
60523         var body = this.templates.body.apply({rows:""});
60524         var html = this.templates.master.apply({
60525             lockedBody: body,
60526             body: body,
60527             lockedHeader: header[0],
60528             header: header[1]
60529         });
60530
60531         //this.updateColumns();
60532
60533         this.grid.getGridEl().dom.innerHTML = html;
60534
60535         this.initElements();
60536         
60537         // a kludge to fix the random scolling effect in webkit
60538         this.el.on("scroll", function() {
60539             this.el.dom.scrollTop=0; // hopefully not recursive..
60540         },this);
60541
60542         this.scroller.on("scroll", this.handleScroll, this);
60543         this.lockedBody.on("mousewheel", this.handleWheel, this);
60544         this.mainBody.on("mousewheel", this.handleWheel, this);
60545
60546         this.mainHd.on("mouseover", this.handleHdOver, this);
60547         this.mainHd.on("mouseout", this.handleHdOut, this);
60548         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60549                 {delegate: "."+this.splitClass});
60550
60551         this.lockedHd.on("mouseover", this.handleHdOver, this);
60552         this.lockedHd.on("mouseout", this.handleHdOut, this);
60553         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60554                 {delegate: "."+this.splitClass});
60555
60556         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60557             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60558         }
60559
60560         this.updateSplitters();
60561
60562         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60563             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60564             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60565         }
60566
60567         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60568             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60569             this.hmenu.add(
60570                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60571                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60572             );
60573             if(this.grid.enableColLock !== false){
60574                 this.hmenu.add('-',
60575                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60576                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60577                 );
60578             }
60579             if (Roo.isTouch) {
60580                  this.hmenu.add('-',
60581                     {id:"wider", text: this.columnsWiderText},
60582                     {id:"narrow", text: this.columnsNarrowText }
60583                 );
60584                 
60585                  
60586             }
60587             
60588             if(this.grid.enableColumnHide !== false){
60589
60590                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60591                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60592                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60593
60594                 this.hmenu.add('-',
60595                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60596                 );
60597             }
60598             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60599
60600             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60601         }
60602
60603         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60604             this.dd = new Roo.grid.GridDragZone(this.grid, {
60605                 ddGroup : this.grid.ddGroup || 'GridDD'
60606             });
60607             
60608         }
60609
60610         /*
60611         for(var i = 0; i < colCount; i++){
60612             if(cm.isHidden(i)){
60613                 this.hideColumn(i);
60614             }
60615             if(cm.config[i].align){
60616                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60617                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60618             }
60619         }*/
60620         
60621         this.updateHeaderSortState();
60622
60623         this.beforeInitialResize();
60624         this.layout(true);
60625
60626         // two part rendering gives faster view to the user
60627         this.renderPhase2.defer(1, this);
60628     },
60629
60630     renderPhase2 : function(){
60631         // render the rows now
60632         this.refresh();
60633         if(this.grid.autoSizeColumns){
60634             this.autoSizeColumns();
60635         }
60636     },
60637
60638     beforeInitialResize : function(){
60639
60640     },
60641
60642     onColumnSplitterMoved : function(i, w){
60643         this.userResized = true;
60644         var cm = this.grid.colModel;
60645         cm.setColumnWidth(i, w, true);
60646         var cid = cm.getColumnId(i);
60647         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60648         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60649         this.updateSplitters();
60650         this.layout();
60651         this.grid.fireEvent("columnresize", i, w);
60652     },
60653
60654     syncRowHeights : function(startIndex, endIndex){
60655         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60656             startIndex = startIndex || 0;
60657             var mrows = this.getBodyTable().rows;
60658             var lrows = this.getLockedTable().rows;
60659             var len = mrows.length-1;
60660             endIndex = Math.min(endIndex || len, len);
60661             for(var i = startIndex; i <= endIndex; i++){
60662                 var m = mrows[i], l = lrows[i];
60663                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60664                 m.style.height = l.style.height = h + "px";
60665             }
60666         }
60667     },
60668
60669     layout : function(initialRender, is2ndPass)
60670     {
60671         var g = this.grid;
60672         var auto = g.autoHeight;
60673         var scrollOffset = 16;
60674         var c = g.getGridEl(), cm = this.cm,
60675                 expandCol = g.autoExpandColumn,
60676                 gv = this;
60677         //c.beginMeasure();
60678
60679         if(!c.dom.offsetWidth){ // display:none?
60680             if(initialRender){
60681                 this.lockedWrap.show();
60682                 this.mainWrap.show();
60683             }
60684             return;
60685         }
60686
60687         var hasLock = this.cm.isLocked(0);
60688
60689         var tbh = this.headerPanel.getHeight();
60690         var bbh = this.footerPanel.getHeight();
60691
60692         if(auto){
60693             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60694             var newHeight = ch + c.getBorderWidth("tb");
60695             if(g.maxHeight){
60696                 newHeight = Math.min(g.maxHeight, newHeight);
60697             }
60698             c.setHeight(newHeight);
60699         }
60700
60701         if(g.autoWidth){
60702             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60703         }
60704
60705         var s = this.scroller;
60706
60707         var csize = c.getSize(true);
60708
60709         this.el.setSize(csize.width, csize.height);
60710
60711         this.headerPanel.setWidth(csize.width);
60712         this.footerPanel.setWidth(csize.width);
60713
60714         var hdHeight = this.mainHd.getHeight();
60715         var vw = csize.width;
60716         var vh = csize.height - (tbh + bbh);
60717
60718         s.setSize(vw, vh);
60719
60720         var bt = this.getBodyTable();
60721         
60722         if(cm.getLockedCount() == cm.config.length){
60723             bt = this.getLockedTable();
60724         }
60725         
60726         var ltWidth = hasLock ?
60727                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60728
60729         var scrollHeight = bt.offsetHeight;
60730         var scrollWidth = ltWidth + bt.offsetWidth;
60731         var vscroll = false, hscroll = false;
60732
60733         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60734
60735         var lw = this.lockedWrap, mw = this.mainWrap;
60736         var lb = this.lockedBody, mb = this.mainBody;
60737
60738         setTimeout(function(){
60739             var t = s.dom.offsetTop;
60740             var w = s.dom.clientWidth,
60741                 h = s.dom.clientHeight;
60742
60743             lw.setTop(t);
60744             lw.setSize(ltWidth, h);
60745
60746             mw.setLeftTop(ltWidth, t);
60747             mw.setSize(w-ltWidth, h);
60748
60749             lb.setHeight(h-hdHeight);
60750             mb.setHeight(h-hdHeight);
60751
60752             if(is2ndPass !== true && !gv.userResized && expandCol){
60753                 // high speed resize without full column calculation
60754                 
60755                 var ci = cm.getIndexById(expandCol);
60756                 if (ci < 0) {
60757                     ci = cm.findColumnIndex(expandCol);
60758                 }
60759                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60760                 var expandId = cm.getColumnId(ci);
60761                 var  tw = cm.getTotalWidth(false);
60762                 var currentWidth = cm.getColumnWidth(ci);
60763                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60764                 if(currentWidth != cw){
60765                     cm.setColumnWidth(ci, cw, true);
60766                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60767                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60768                     gv.updateSplitters();
60769                     gv.layout(false, true);
60770                 }
60771             }
60772
60773             if(initialRender){
60774                 lw.show();
60775                 mw.show();
60776             }
60777             //c.endMeasure();
60778         }, 10);
60779     },
60780
60781     onWindowResize : function(){
60782         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60783             return;
60784         }
60785         this.layout();
60786     },
60787
60788     appendFooter : function(parentEl){
60789         return null;
60790     },
60791
60792     sortAscText : "Sort Ascending",
60793     sortDescText : "Sort Descending",
60794     lockText : "Lock Column",
60795     unlockText : "Unlock Column",
60796     columnsText : "Columns",
60797  
60798     columnsWiderText : "Wider",
60799     columnsNarrowText : "Thinner"
60800 });
60801
60802
60803 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60804     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60805     this.proxy.el.addClass('x-grid3-col-dd');
60806 };
60807
60808 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60809     handleMouseDown : function(e){
60810
60811     },
60812
60813     callHandleMouseDown : function(e){
60814         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60815     }
60816 });
60817 /*
60818  * Based on:
60819  * Ext JS Library 1.1.1
60820  * Copyright(c) 2006-2007, Ext JS, LLC.
60821  *
60822  * Originally Released Under LGPL - original licence link has changed is not relivant.
60823  *
60824  * Fork - LGPL
60825  * <script type="text/javascript">
60826  */
60827  /**
60828  * @extends Roo.dd.DDProxy
60829  * @class Roo.grid.SplitDragZone
60830  * Support for Column Header resizing
60831  * @constructor
60832  * @param {Object} config
60833  */
60834 // private
60835 // This is a support class used internally by the Grid components
60836 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60837     this.grid = grid;
60838     this.view = grid.getView();
60839     this.proxy = this.view.resizeProxy;
60840     Roo.grid.SplitDragZone.superclass.constructor.call(
60841         this,
60842         hd, // ID
60843         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60844         {  // CONFIG
60845             dragElId : Roo.id(this.proxy.dom),
60846             resizeFrame:false
60847         }
60848     );
60849     
60850     this.setHandleElId(Roo.id(hd));
60851     if (hd2 !== false) {
60852         this.setOuterHandleElId(Roo.id(hd2));
60853     }
60854     
60855     this.scroll = false;
60856 };
60857 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60858     fly: Roo.Element.fly,
60859
60860     b4StartDrag : function(x, y){
60861         this.view.headersDisabled = true;
60862         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60863                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60864         );
60865         this.proxy.setHeight(h);
60866         
60867         // for old system colWidth really stored the actual width?
60868         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60869         // which in reality did not work.. - it worked only for fixed sizes
60870         // for resizable we need to use actual sizes.
60871         var w = this.cm.getColumnWidth(this.cellIndex);
60872         if (!this.view.mainWrap) {
60873             // bootstrap.
60874             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60875         }
60876         
60877         
60878         
60879         // this was w-this.grid.minColumnWidth;
60880         // doesnt really make sense? - w = thie curren width or the rendered one?
60881         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60882         this.resetConstraints();
60883         this.setXConstraint(minw, 1000);
60884         this.setYConstraint(0, 0);
60885         this.minX = x - minw;
60886         this.maxX = x + 1000;
60887         this.startPos = x;
60888         if (!this.view.mainWrap) { // this is Bootstrap code..
60889             this.getDragEl().style.display='block';
60890         }
60891         
60892         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60893     },
60894
60895
60896     handleMouseDown : function(e){
60897         ev = Roo.EventObject.setEvent(e);
60898         var t = this.fly(ev.getTarget());
60899         if(t.hasClass("x-grid-split")){
60900             this.cellIndex = this.view.getCellIndex(t.dom);
60901             this.split = t.dom;
60902             this.cm = this.grid.colModel;
60903             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60904                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60905             }
60906         }
60907     },
60908
60909     endDrag : function(e){
60910         this.view.headersDisabled = false;
60911         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60912         var diff = endX - this.startPos;
60913         // 
60914         var w = this.cm.getColumnWidth(this.cellIndex);
60915         if (!this.view.mainWrap) {
60916             w = 0;
60917         }
60918         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60919     },
60920
60921     autoOffset : function(){
60922         this.setDelta(0,0);
60923     }
60924 });/*
60925  * Based on:
60926  * Ext JS Library 1.1.1
60927  * Copyright(c) 2006-2007, Ext JS, LLC.
60928  *
60929  * Originally Released Under LGPL - original licence link has changed is not relivant.
60930  *
60931  * Fork - LGPL
60932  * <script type="text/javascript">
60933  */
60934  
60935 // private
60936 // This is a support class used internally by the Grid components
60937 Roo.grid.GridDragZone = function(grid, config){
60938     this.view = grid.getView();
60939     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60940     if(this.view.lockedBody){
60941         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60942         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60943     }
60944     this.scroll = false;
60945     this.grid = grid;
60946     this.ddel = document.createElement('div');
60947     this.ddel.className = 'x-grid-dd-wrap';
60948 };
60949
60950 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60951     ddGroup : "GridDD",
60952
60953     getDragData : function(e){
60954         var t = Roo.lib.Event.getTarget(e);
60955         var rowIndex = this.view.findRowIndex(t);
60956         var sm = this.grid.selModel;
60957             
60958         //Roo.log(rowIndex);
60959         
60960         if (sm.getSelectedCell) {
60961             // cell selection..
60962             if (!sm.getSelectedCell()) {
60963                 return false;
60964             }
60965             if (rowIndex != sm.getSelectedCell()[0]) {
60966                 return false;
60967             }
60968         
60969         }
60970         if (sm.getSelections && sm.getSelections().length < 1) {
60971             return false;
60972         }
60973         
60974         
60975         // before it used to all dragging of unseleted... - now we dont do that.
60976         if(rowIndex !== false){
60977             
60978             // if editorgrid.. 
60979             
60980             
60981             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60982                
60983             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60984               //  
60985             //}
60986             if (e.hasModifier()){
60987                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60988             }
60989             
60990             Roo.log("getDragData");
60991             
60992             return {
60993                 grid: this.grid,
60994                 ddel: this.ddel,
60995                 rowIndex: rowIndex,
60996                 selections: sm.getSelections ? sm.getSelections() : (
60997                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60998             };
60999         }
61000         return false;
61001     },
61002     
61003     
61004     onInitDrag : function(e){
61005         var data = this.dragData;
61006         this.ddel.innerHTML = this.grid.getDragDropText();
61007         this.proxy.update(this.ddel);
61008         // fire start drag?
61009     },
61010
61011     afterRepair : function(){
61012         this.dragging = false;
61013     },
61014
61015     getRepairXY : function(e, data){
61016         return false;
61017     },
61018
61019     onEndDrag : function(data, e){
61020         // fire end drag?
61021     },
61022
61023     onValidDrop : function(dd, e, id){
61024         // fire drag drop?
61025         this.hideProxy();
61026     },
61027
61028     beforeInvalidDrop : function(e, id){
61029
61030     }
61031 });/*
61032  * Based on:
61033  * Ext JS Library 1.1.1
61034  * Copyright(c) 2006-2007, Ext JS, LLC.
61035  *
61036  * Originally Released Under LGPL - original licence link has changed is not relivant.
61037  *
61038  * Fork - LGPL
61039  * <script type="text/javascript">
61040  */
61041  
61042
61043 /**
61044  * @class Roo.grid.ColumnModel
61045  * @extends Roo.util.Observable
61046  * This is the default implementation of a ColumnModel used by the Grid. It defines
61047  * the columns in the grid.
61048  * <br>Usage:<br>
61049  <pre><code>
61050  var colModel = new Roo.grid.ColumnModel([
61051         {header: "Ticker", width: 60, sortable: true, locked: true},
61052         {header: "Company Name", width: 150, sortable: true},
61053         {header: "Market Cap.", width: 100, sortable: true},
61054         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61055         {header: "Employees", width: 100, sortable: true, resizable: false}
61056  ]);
61057  </code></pre>
61058  * <p>
61059  
61060  * The config options listed for this class are options which may appear in each
61061  * individual column definition.
61062  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61063  * @constructor
61064  * @param {Object} config An Array of column config objects. See this class's
61065  * config objects for details.
61066 */
61067 Roo.grid.ColumnModel = function(config){
61068         /**
61069      * The config passed into the constructor
61070      */
61071     this.config = []; //config;
61072     this.lookup = {};
61073
61074     // if no id, create one
61075     // if the column does not have a dataIndex mapping,
61076     // map it to the order it is in the config
61077     for(var i = 0, len = config.length; i < len; i++){
61078         this.addColumn(config[i]);
61079         
61080     }
61081
61082     /**
61083      * The width of columns which have no width specified (defaults to 100)
61084      * @type Number
61085      */
61086     this.defaultWidth = 100;
61087
61088     /**
61089      * Default sortable of columns which have no sortable specified (defaults to false)
61090      * @type Boolean
61091      */
61092     this.defaultSortable = false;
61093
61094     this.addEvents({
61095         /**
61096              * @event widthchange
61097              * Fires when the width of a column changes.
61098              * @param {ColumnModel} this
61099              * @param {Number} columnIndex The column index
61100              * @param {Number} newWidth The new width
61101              */
61102             "widthchange": true,
61103         /**
61104              * @event headerchange
61105              * Fires when the text of a header changes.
61106              * @param {ColumnModel} this
61107              * @param {Number} columnIndex The column index
61108              * @param {Number} newText The new header text
61109              */
61110             "headerchange": true,
61111         /**
61112              * @event hiddenchange
61113              * Fires when a column is hidden or "unhidden".
61114              * @param {ColumnModel} this
61115              * @param {Number} columnIndex The column index
61116              * @param {Boolean} hidden true if hidden, false otherwise
61117              */
61118             "hiddenchange": true,
61119             /**
61120          * @event columnmoved
61121          * Fires when a column is moved.
61122          * @param {ColumnModel} this
61123          * @param {Number} oldIndex
61124          * @param {Number} newIndex
61125          */
61126         "columnmoved" : true,
61127         /**
61128          * @event columlockchange
61129          * Fires when a column's locked state is changed
61130          * @param {ColumnModel} this
61131          * @param {Number} colIndex
61132          * @param {Boolean} locked true if locked
61133          */
61134         "columnlockchange" : true
61135     });
61136     Roo.grid.ColumnModel.superclass.constructor.call(this);
61137 };
61138 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61139     /**
61140      * @cfg {String} header The header text to display in the Grid view.
61141      */
61142         /**
61143      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61144      */
61145         /**
61146      * @cfg {String} smHeader Header at Bootsrap Small width
61147      */
61148         /**
61149      * @cfg {String} mdHeader Header at Bootsrap Medium width
61150      */
61151         /**
61152      * @cfg {String} lgHeader Header at Bootsrap Large width
61153      */
61154         /**
61155      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61156      */
61157     /**
61158      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61159      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61160      * specified, the column's index is used as an index into the Record's data Array.
61161      */
61162     /**
61163      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61164      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61165      */
61166     /**
61167      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61168      * Defaults to the value of the {@link #defaultSortable} property.
61169      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61170      */
61171     /**
61172      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61173      */
61174     /**
61175      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61176      */
61177     /**
61178      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61179      */
61180     /**
61181      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61182      */
61183     /**
61184      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61185      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61186      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61187      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61188      */
61189        /**
61190      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61191      */
61192     /**
61193      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61194      */
61195     /**
61196      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61197      */
61198     /**
61199      * @cfg {String} cursor (Optional)
61200      */
61201     /**
61202      * @cfg {String} tooltip (Optional)
61203      */
61204     /**
61205      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61206      */
61207     /**
61208      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61209      */
61210     /**
61211      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61212      */
61213     /**
61214      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61215      */
61216         /**
61217      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61218      */
61219     /**
61220      * Returns the id of the column at the specified index.
61221      * @param {Number} index The column index
61222      * @return {String} the id
61223      */
61224     getColumnId : function(index){
61225         return this.config[index].id;
61226     },
61227
61228     /**
61229      * Returns the column for a specified id.
61230      * @param {String} id The column id
61231      * @return {Object} the column
61232      */
61233     getColumnById : function(id){
61234         return this.lookup[id];
61235     },
61236
61237     
61238     /**
61239      * Returns the column Object for a specified dataIndex.
61240      * @param {String} dataIndex The column dataIndex
61241      * @return {Object|Boolean} the column or false if not found
61242      */
61243     getColumnByDataIndex: function(dataIndex){
61244         var index = this.findColumnIndex(dataIndex);
61245         return index > -1 ? this.config[index] : false;
61246     },
61247     
61248     /**
61249      * Returns the index for a specified column id.
61250      * @param {String} id The column id
61251      * @return {Number} the index, or -1 if not found
61252      */
61253     getIndexById : function(id){
61254         for(var i = 0, len = this.config.length; i < len; i++){
61255             if(this.config[i].id == id){
61256                 return i;
61257             }
61258         }
61259         return -1;
61260     },
61261     
61262     /**
61263      * Returns the index for a specified column dataIndex.
61264      * @param {String} dataIndex The column dataIndex
61265      * @return {Number} the index, or -1 if not found
61266      */
61267     
61268     findColumnIndex : function(dataIndex){
61269         for(var i = 0, len = this.config.length; i < len; i++){
61270             if(this.config[i].dataIndex == dataIndex){
61271                 return i;
61272             }
61273         }
61274         return -1;
61275     },
61276     
61277     
61278     moveColumn : function(oldIndex, newIndex){
61279         var c = this.config[oldIndex];
61280         this.config.splice(oldIndex, 1);
61281         this.config.splice(newIndex, 0, c);
61282         this.dataMap = null;
61283         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61284     },
61285
61286     isLocked : function(colIndex){
61287         return this.config[colIndex].locked === true;
61288     },
61289
61290     setLocked : function(colIndex, value, suppressEvent){
61291         if(this.isLocked(colIndex) == value){
61292             return;
61293         }
61294         this.config[colIndex].locked = value;
61295         if(!suppressEvent){
61296             this.fireEvent("columnlockchange", this, colIndex, value);
61297         }
61298     },
61299
61300     getTotalLockedWidth : function(){
61301         var totalWidth = 0;
61302         for(var i = 0; i < this.config.length; i++){
61303             if(this.isLocked(i) && !this.isHidden(i)){
61304                 this.totalWidth += this.getColumnWidth(i);
61305             }
61306         }
61307         return totalWidth;
61308     },
61309
61310     getLockedCount : function(){
61311         for(var i = 0, len = this.config.length; i < len; i++){
61312             if(!this.isLocked(i)){
61313                 return i;
61314             }
61315         }
61316         
61317         return this.config.length;
61318     },
61319
61320     /**
61321      * Returns the number of columns.
61322      * @return {Number}
61323      */
61324     getColumnCount : function(visibleOnly){
61325         if(visibleOnly === true){
61326             var c = 0;
61327             for(var i = 0, len = this.config.length; i < len; i++){
61328                 if(!this.isHidden(i)){
61329                     c++;
61330                 }
61331             }
61332             return c;
61333         }
61334         return this.config.length;
61335     },
61336
61337     /**
61338      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61339      * @param {Function} fn
61340      * @param {Object} scope (optional)
61341      * @return {Array} result
61342      */
61343     getColumnsBy : function(fn, scope){
61344         var r = [];
61345         for(var i = 0, len = this.config.length; i < len; i++){
61346             var c = this.config[i];
61347             if(fn.call(scope||this, c, i) === true){
61348                 r[r.length] = c;
61349             }
61350         }
61351         return r;
61352     },
61353
61354     /**
61355      * Returns true if the specified column is sortable.
61356      * @param {Number} col The column index
61357      * @return {Boolean}
61358      */
61359     isSortable : function(col){
61360         if(typeof this.config[col].sortable == "undefined"){
61361             return this.defaultSortable;
61362         }
61363         return this.config[col].sortable;
61364     },
61365
61366     /**
61367      * Returns the rendering (formatting) function defined for the column.
61368      * @param {Number} col The column index.
61369      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61370      */
61371     getRenderer : function(col){
61372         if(!this.config[col].renderer){
61373             return Roo.grid.ColumnModel.defaultRenderer;
61374         }
61375         return this.config[col].renderer;
61376     },
61377
61378     /**
61379      * Sets the rendering (formatting) function for a column.
61380      * @param {Number} col The column index
61381      * @param {Function} fn The function to use to process the cell's raw data
61382      * to return HTML markup for the grid view. The render function is called with
61383      * the following parameters:<ul>
61384      * <li>Data value.</li>
61385      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61386      * <li>css A CSS style string to apply to the table cell.</li>
61387      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61388      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61389      * <li>Row index</li>
61390      * <li>Column index</li>
61391      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61392      */
61393     setRenderer : function(col, fn){
61394         this.config[col].renderer = fn;
61395     },
61396
61397     /**
61398      * Returns the width for the specified column.
61399      * @param {Number} col The column index
61400      * @param (optional) {String} gridSize bootstrap width size.
61401      * @return {Number}
61402      */
61403     getColumnWidth : function(col, gridSize)
61404         {
61405                 var cfg = this.config[col];
61406                 
61407                 if (typeof(gridSize) == 'undefined') {
61408                         return cfg.width * 1 || this.defaultWidth;
61409                 }
61410                 if (gridSize === false) { // if we set it..
61411                         return cfg.width || false;
61412                 }
61413                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61414                 
61415                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61416                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61417                                 continue;
61418                         }
61419                         return cfg[ sizes[i] ];
61420                 }
61421                 return 1;
61422                 
61423     },
61424
61425     /**
61426      * Sets the width for a column.
61427      * @param {Number} col The column index
61428      * @param {Number} width The new width
61429      */
61430     setColumnWidth : function(col, width, suppressEvent){
61431         this.config[col].width = width;
61432         this.totalWidth = null;
61433         if(!suppressEvent){
61434              this.fireEvent("widthchange", this, col, width);
61435         }
61436     },
61437
61438     /**
61439      * Returns the total width of all columns.
61440      * @param {Boolean} includeHidden True to include hidden column widths
61441      * @return {Number}
61442      */
61443     getTotalWidth : function(includeHidden){
61444         if(!this.totalWidth){
61445             this.totalWidth = 0;
61446             for(var i = 0, len = this.config.length; i < len; i++){
61447                 if(includeHidden || !this.isHidden(i)){
61448                     this.totalWidth += this.getColumnWidth(i);
61449                 }
61450             }
61451         }
61452         return this.totalWidth;
61453     },
61454
61455     /**
61456      * Returns the header for the specified column.
61457      * @param {Number} col The column index
61458      * @return {String}
61459      */
61460     getColumnHeader : function(col){
61461         return this.config[col].header;
61462     },
61463
61464     /**
61465      * Sets the header for a column.
61466      * @param {Number} col The column index
61467      * @param {String} header The new header
61468      */
61469     setColumnHeader : function(col, header){
61470         this.config[col].header = header;
61471         this.fireEvent("headerchange", this, col, header);
61472     },
61473
61474     /**
61475      * Returns the tooltip for the specified column.
61476      * @param {Number} col The column index
61477      * @return {String}
61478      */
61479     getColumnTooltip : function(col){
61480             return this.config[col].tooltip;
61481     },
61482     /**
61483      * Sets the tooltip for a column.
61484      * @param {Number} col The column index
61485      * @param {String} tooltip The new tooltip
61486      */
61487     setColumnTooltip : function(col, tooltip){
61488             this.config[col].tooltip = tooltip;
61489     },
61490
61491     /**
61492      * Returns the dataIndex for the specified column.
61493      * @param {Number} col The column index
61494      * @return {Number}
61495      */
61496     getDataIndex : function(col){
61497         return this.config[col].dataIndex;
61498     },
61499
61500     /**
61501      * Sets the dataIndex for a column.
61502      * @param {Number} col The column index
61503      * @param {Number} dataIndex The new dataIndex
61504      */
61505     setDataIndex : function(col, dataIndex){
61506         this.config[col].dataIndex = dataIndex;
61507     },
61508
61509     
61510     
61511     /**
61512      * Returns true if the cell is editable.
61513      * @param {Number} colIndex The column index
61514      * @param {Number} rowIndex The row index - this is nto actually used..?
61515      * @return {Boolean}
61516      */
61517     isCellEditable : function(colIndex, rowIndex){
61518         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61519     },
61520
61521     /**
61522      * Returns the editor defined for the cell/column.
61523      * return false or null to disable editing.
61524      * @param {Number} colIndex The column index
61525      * @param {Number} rowIndex The row index
61526      * @return {Object}
61527      */
61528     getCellEditor : function(colIndex, rowIndex){
61529         return this.config[colIndex].editor;
61530     },
61531
61532     /**
61533      * Sets if a column is editable.
61534      * @param {Number} col The column index
61535      * @param {Boolean} editable True if the column is editable
61536      */
61537     setEditable : function(col, editable){
61538         this.config[col].editable = editable;
61539     },
61540
61541
61542     /**
61543      * Returns true if the column is hidden.
61544      * @param {Number} colIndex The column index
61545      * @return {Boolean}
61546      */
61547     isHidden : function(colIndex){
61548         return this.config[colIndex].hidden;
61549     },
61550
61551
61552     /**
61553      * Returns true if the column width cannot be changed
61554      */
61555     isFixed : function(colIndex){
61556         return this.config[colIndex].fixed;
61557     },
61558
61559     /**
61560      * Returns true if the column can be resized
61561      * @return {Boolean}
61562      */
61563     isResizable : function(colIndex){
61564         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61565     },
61566     /**
61567      * Sets if a column is hidden.
61568      * @param {Number} colIndex The column index
61569      * @param {Boolean} hidden True if the column is hidden
61570      */
61571     setHidden : function(colIndex, hidden){
61572         this.config[colIndex].hidden = hidden;
61573         this.totalWidth = null;
61574         this.fireEvent("hiddenchange", this, colIndex, hidden);
61575     },
61576
61577     /**
61578      * Sets the editor for a column.
61579      * @param {Number} col The column index
61580      * @param {Object} editor The editor object
61581      */
61582     setEditor : function(col, editor){
61583         this.config[col].editor = editor;
61584     },
61585     /**
61586      * Add a column (experimental...) - defaults to adding to the end..
61587      * @param {Object} config 
61588     */
61589     addColumn : function(c)
61590     {
61591     
61592         var i = this.config.length;
61593         this.config[i] = c;
61594         
61595         if(typeof c.dataIndex == "undefined"){
61596             c.dataIndex = i;
61597         }
61598         if(typeof c.renderer == "string"){
61599             c.renderer = Roo.util.Format[c.renderer];
61600         }
61601         if(typeof c.id == "undefined"){
61602             c.id = Roo.id();
61603         }
61604         if(c.editor && c.editor.xtype){
61605             c.editor  = Roo.factory(c.editor, Roo.grid);
61606         }
61607         if(c.editor && c.editor.isFormField){
61608             c.editor = new Roo.grid.GridEditor(c.editor);
61609         }
61610         this.lookup[c.id] = c;
61611     }
61612     
61613 });
61614
61615 Roo.grid.ColumnModel.defaultRenderer = function(value)
61616 {
61617     if(typeof value == "object") {
61618         return value;
61619     }
61620         if(typeof value == "string" && value.length < 1){
61621             return "&#160;";
61622         }
61623     
61624         return String.format("{0}", value);
61625 };
61626
61627 // Alias for backwards compatibility
61628 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61629 /*
61630  * Based on:
61631  * Ext JS Library 1.1.1
61632  * Copyright(c) 2006-2007, Ext JS, LLC.
61633  *
61634  * Originally Released Under LGPL - original licence link has changed is not relivant.
61635  *
61636  * Fork - LGPL
61637  * <script type="text/javascript">
61638  */
61639
61640 /**
61641  * @class Roo.grid.AbstractSelectionModel
61642  * @extends Roo.util.Observable
61643  * @abstract
61644  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61645  * implemented by descendant classes.  This class should not be directly instantiated.
61646  * @constructor
61647  */
61648 Roo.grid.AbstractSelectionModel = function(){
61649     this.locked = false;
61650     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61651 };
61652
61653 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61654     /** @ignore Called by the grid automatically. Do not call directly. */
61655     init : function(grid){
61656         this.grid = grid;
61657         this.initEvents();
61658     },
61659
61660     /**
61661      * Locks the selections.
61662      */
61663     lock : function(){
61664         this.locked = true;
61665     },
61666
61667     /**
61668      * Unlocks the selections.
61669      */
61670     unlock : function(){
61671         this.locked = false;
61672     },
61673
61674     /**
61675      * Returns true if the selections are locked.
61676      * @return {Boolean}
61677      */
61678     isLocked : function(){
61679         return this.locked;
61680     }
61681 });/*
61682  * Based on:
61683  * Ext JS Library 1.1.1
61684  * Copyright(c) 2006-2007, Ext JS, LLC.
61685  *
61686  * Originally Released Under LGPL - original licence link has changed is not relivant.
61687  *
61688  * Fork - LGPL
61689  * <script type="text/javascript">
61690  */
61691 /**
61692  * @extends Roo.grid.AbstractSelectionModel
61693  * @class Roo.grid.RowSelectionModel
61694  * The default SelectionModel used by {@link Roo.grid.Grid}.
61695  * It supports multiple selections and keyboard selection/navigation. 
61696  * @constructor
61697  * @param {Object} config
61698  */
61699 Roo.grid.RowSelectionModel = function(config){
61700     Roo.apply(this, config);
61701     this.selections = new Roo.util.MixedCollection(false, function(o){
61702         return o.id;
61703     });
61704
61705     this.last = false;
61706     this.lastActive = false;
61707
61708     this.addEvents({
61709         /**
61710         * @event selectionchange
61711         * Fires when the selection changes
61712         * @param {SelectionModel} this
61713         */
61714        "selectionchange" : true,
61715        /**
61716         * @event afterselectionchange
61717         * Fires after the selection changes (eg. by key press or clicking)
61718         * @param {SelectionModel} this
61719         */
61720        "afterselectionchange" : true,
61721        /**
61722         * @event beforerowselect
61723         * Fires when a row is selected being selected, return false to cancel.
61724         * @param {SelectionModel} this
61725         * @param {Number} rowIndex The selected index
61726         * @param {Boolean} keepExisting False if other selections will be cleared
61727         */
61728        "beforerowselect" : true,
61729        /**
61730         * @event rowselect
61731         * Fires when a row is selected.
61732         * @param {SelectionModel} this
61733         * @param {Number} rowIndex The selected index
61734         * @param {Roo.data.Record} r The record
61735         */
61736        "rowselect" : true,
61737        /**
61738         * @event rowdeselect
61739         * Fires when a row is deselected.
61740         * @param {SelectionModel} this
61741         * @param {Number} rowIndex The selected index
61742         */
61743         "rowdeselect" : true
61744     });
61745     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61746     this.locked = false;
61747 };
61748
61749 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61750     /**
61751      * @cfg {Boolean} singleSelect
61752      * True to allow selection of only one row at a time (defaults to false)
61753      */
61754     singleSelect : false,
61755
61756     // private
61757     initEvents : function(){
61758
61759         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61760             this.grid.on("mousedown", this.handleMouseDown, this);
61761         }else{ // allow click to work like normal
61762             this.grid.on("rowclick", this.handleDragableRowClick, this);
61763         }
61764         // bootstrap does not have a view..
61765         var view = this.grid.view ? this.grid.view : this.grid;
61766         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61767             "up" : function(e){
61768                 if(!e.shiftKey){
61769                     this.selectPrevious(e.shiftKey);
61770                 }else if(this.last !== false && this.lastActive !== false){
61771                     var last = this.last;
61772                     this.selectRange(this.last,  this.lastActive-1);
61773                     view.focusRow(this.lastActive);
61774                     if(last !== false){
61775                         this.last = last;
61776                     }
61777                 }else{
61778                     this.selectFirstRow();
61779                 }
61780                 this.fireEvent("afterselectionchange", this);
61781             },
61782             "down" : function(e){
61783                 if(!e.shiftKey){
61784                     this.selectNext(e.shiftKey);
61785                 }else if(this.last !== false && this.lastActive !== false){
61786                     var last = this.last;
61787                     this.selectRange(this.last,  this.lastActive+1);
61788                     view.focusRow(this.lastActive);
61789                     if(last !== false){
61790                         this.last = last;
61791                     }
61792                 }else{
61793                     this.selectFirstRow();
61794                 }
61795                 this.fireEvent("afterselectionchange", this);
61796             },
61797             scope: this
61798         });
61799
61800          
61801         view.on("refresh", this.onRefresh, this);
61802         view.on("rowupdated", this.onRowUpdated, this);
61803         view.on("rowremoved", this.onRemove, this);
61804     },
61805
61806     // private
61807     onRefresh : function(){
61808         var ds = this.grid.ds, i, v = this.grid.view;
61809         var s = this.selections;
61810         s.each(function(r){
61811             if((i = ds.indexOfId(r.id)) != -1){
61812                 v.onRowSelect(i);
61813                 s.add(ds.getAt(i)); // updating the selection relate data
61814             }else{
61815                 s.remove(r);
61816             }
61817         });
61818     },
61819
61820     // private
61821     onRemove : function(v, index, r){
61822         this.selections.remove(r);
61823     },
61824
61825     // private
61826     onRowUpdated : function(v, index, r){
61827         if(this.isSelected(r)){
61828             v.onRowSelect(index);
61829         }
61830     },
61831
61832     /**
61833      * Select records.
61834      * @param {Array} records The records to select
61835      * @param {Boolean} keepExisting (optional) True to keep existing selections
61836      */
61837     selectRecords : function(records, keepExisting){
61838         if(!keepExisting){
61839             this.clearSelections();
61840         }
61841         var ds = this.grid.ds;
61842         for(var i = 0, len = records.length; i < len; i++){
61843             this.selectRow(ds.indexOf(records[i]), true);
61844         }
61845     },
61846
61847     /**
61848      * Gets the number of selected rows.
61849      * @return {Number}
61850      */
61851     getCount : function(){
61852         return this.selections.length;
61853     },
61854
61855     /**
61856      * Selects the first row in the grid.
61857      */
61858     selectFirstRow : function(){
61859         this.selectRow(0);
61860     },
61861
61862     /**
61863      * Select the last row.
61864      * @param {Boolean} keepExisting (optional) True to keep existing selections
61865      */
61866     selectLastRow : function(keepExisting){
61867         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61868     },
61869
61870     /**
61871      * Selects the row immediately following the last selected row.
61872      * @param {Boolean} keepExisting (optional) True to keep existing selections
61873      */
61874     selectNext : function(keepExisting){
61875         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61876             this.selectRow(this.last+1, keepExisting);
61877             var view = this.grid.view ? this.grid.view : this.grid;
61878             view.focusRow(this.last);
61879         }
61880     },
61881
61882     /**
61883      * Selects the row that precedes the last selected row.
61884      * @param {Boolean} keepExisting (optional) True to keep existing selections
61885      */
61886     selectPrevious : function(keepExisting){
61887         if(this.last){
61888             this.selectRow(this.last-1, keepExisting);
61889             var view = this.grid.view ? this.grid.view : this.grid;
61890             view.focusRow(this.last);
61891         }
61892     },
61893
61894     /**
61895      * Returns the selected records
61896      * @return {Array} Array of selected records
61897      */
61898     getSelections : function(){
61899         return [].concat(this.selections.items);
61900     },
61901
61902     /**
61903      * Returns the first selected record.
61904      * @return {Record}
61905      */
61906     getSelected : function(){
61907         return this.selections.itemAt(0);
61908     },
61909
61910
61911     /**
61912      * Clears all selections.
61913      */
61914     clearSelections : function(fast){
61915         if(this.locked) {
61916             return;
61917         }
61918         if(fast !== true){
61919             var ds = this.grid.ds;
61920             var s = this.selections;
61921             s.each(function(r){
61922                 this.deselectRow(ds.indexOfId(r.id));
61923             }, this);
61924             s.clear();
61925         }else{
61926             this.selections.clear();
61927         }
61928         this.last = false;
61929     },
61930
61931
61932     /**
61933      * Selects all rows.
61934      */
61935     selectAll : function(){
61936         if(this.locked) {
61937             return;
61938         }
61939         this.selections.clear();
61940         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61941             this.selectRow(i, true);
61942         }
61943     },
61944
61945     /**
61946      * Returns True if there is a selection.
61947      * @return {Boolean}
61948      */
61949     hasSelection : function(){
61950         return this.selections.length > 0;
61951     },
61952
61953     /**
61954      * Returns True if the specified row is selected.
61955      * @param {Number/Record} record The record or index of the record to check
61956      * @return {Boolean}
61957      */
61958     isSelected : function(index){
61959         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61960         return (r && this.selections.key(r.id) ? true : false);
61961     },
61962
61963     /**
61964      * Returns True if the specified record id is selected.
61965      * @param {String} id The id of record to check
61966      * @return {Boolean}
61967      */
61968     isIdSelected : function(id){
61969         return (this.selections.key(id) ? true : false);
61970     },
61971
61972     // private
61973     handleMouseDown : function(e, t)
61974     {
61975         var view = this.grid.view ? this.grid.view : this.grid;
61976         var rowIndex;
61977         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61978             return;
61979         };
61980         if(e.shiftKey && this.last !== false){
61981             var last = this.last;
61982             this.selectRange(last, rowIndex, e.ctrlKey);
61983             this.last = last; // reset the last
61984             view.focusRow(rowIndex);
61985         }else{
61986             var isSelected = this.isSelected(rowIndex);
61987             if(e.button !== 0 && isSelected){
61988                 view.focusRow(rowIndex);
61989             }else if(e.ctrlKey && isSelected){
61990                 this.deselectRow(rowIndex);
61991             }else if(!isSelected){
61992                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61993                 view.focusRow(rowIndex);
61994             }
61995         }
61996         this.fireEvent("afterselectionchange", this);
61997     },
61998     // private
61999     handleDragableRowClick :  function(grid, rowIndex, e) 
62000     {
62001         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62002             this.selectRow(rowIndex, false);
62003             var view = this.grid.view ? this.grid.view : this.grid;
62004             view.focusRow(rowIndex);
62005              this.fireEvent("afterselectionchange", this);
62006         }
62007     },
62008     
62009     /**
62010      * Selects multiple rows.
62011      * @param {Array} rows Array of the indexes of the row to select
62012      * @param {Boolean} keepExisting (optional) True to keep existing selections
62013      */
62014     selectRows : function(rows, keepExisting){
62015         if(!keepExisting){
62016             this.clearSelections();
62017         }
62018         for(var i = 0, len = rows.length; i < len; i++){
62019             this.selectRow(rows[i], true);
62020         }
62021     },
62022
62023     /**
62024      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62025      * @param {Number} startRow The index of the first row in the range
62026      * @param {Number} endRow The index of the last row in the range
62027      * @param {Boolean} keepExisting (optional) True to retain existing selections
62028      */
62029     selectRange : function(startRow, endRow, keepExisting){
62030         if(this.locked) {
62031             return;
62032         }
62033         if(!keepExisting){
62034             this.clearSelections();
62035         }
62036         if(startRow <= endRow){
62037             for(var i = startRow; i <= endRow; i++){
62038                 this.selectRow(i, true);
62039             }
62040         }else{
62041             for(var i = startRow; i >= endRow; i--){
62042                 this.selectRow(i, true);
62043             }
62044         }
62045     },
62046
62047     /**
62048      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62049      * @param {Number} startRow The index of the first row in the range
62050      * @param {Number} endRow The index of the last row in the range
62051      */
62052     deselectRange : function(startRow, endRow, preventViewNotify){
62053         if(this.locked) {
62054             return;
62055         }
62056         for(var i = startRow; i <= endRow; i++){
62057             this.deselectRow(i, preventViewNotify);
62058         }
62059     },
62060
62061     /**
62062      * Selects a row.
62063      * @param {Number} row The index of the row to select
62064      * @param {Boolean} keepExisting (optional) True to keep existing selections
62065      */
62066     selectRow : function(index, keepExisting, preventViewNotify){
62067         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62068             return;
62069         }
62070         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62071             if(!keepExisting || this.singleSelect){
62072                 this.clearSelections();
62073             }
62074             var r = this.grid.ds.getAt(index);
62075             this.selections.add(r);
62076             this.last = this.lastActive = index;
62077             if(!preventViewNotify){
62078                 var view = this.grid.view ? this.grid.view : this.grid;
62079                 view.onRowSelect(index);
62080             }
62081             this.fireEvent("rowselect", this, index, r);
62082             this.fireEvent("selectionchange", this);
62083         }
62084     },
62085
62086     /**
62087      * Deselects a row.
62088      * @param {Number} row The index of the row to deselect
62089      */
62090     deselectRow : function(index, preventViewNotify){
62091         if(this.locked) {
62092             return;
62093         }
62094         if(this.last == index){
62095             this.last = false;
62096         }
62097         if(this.lastActive == index){
62098             this.lastActive = false;
62099         }
62100         var r = this.grid.ds.getAt(index);
62101         this.selections.remove(r);
62102         if(!preventViewNotify){
62103             var view = this.grid.view ? this.grid.view : this.grid;
62104             view.onRowDeselect(index);
62105         }
62106         this.fireEvent("rowdeselect", this, index);
62107         this.fireEvent("selectionchange", this);
62108     },
62109
62110     // private
62111     restoreLast : function(){
62112         if(this._last){
62113             this.last = this._last;
62114         }
62115     },
62116
62117     // private
62118     acceptsNav : function(row, col, cm){
62119         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62120     },
62121
62122     // private
62123     onEditorKey : function(field, e){
62124         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62125         if(k == e.TAB){
62126             e.stopEvent();
62127             ed.completeEdit();
62128             if(e.shiftKey){
62129                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62130             }else{
62131                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62132             }
62133         }else if(k == e.ENTER && !e.ctrlKey){
62134             e.stopEvent();
62135             ed.completeEdit();
62136             if(e.shiftKey){
62137                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62138             }else{
62139                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62140             }
62141         }else if(k == e.ESC){
62142             ed.cancelEdit();
62143         }
62144         if(newCell){
62145             g.startEditing(newCell[0], newCell[1]);
62146         }
62147     }
62148 });/*
62149  * Based on:
62150  * Ext JS Library 1.1.1
62151  * Copyright(c) 2006-2007, Ext JS, LLC.
62152  *
62153  * Originally Released Under LGPL - original licence link has changed is not relivant.
62154  *
62155  * Fork - LGPL
62156  * <script type="text/javascript">
62157  */
62158 /**
62159  * @class Roo.grid.CellSelectionModel
62160  * @extends Roo.grid.AbstractSelectionModel
62161  * This class provides the basic implementation for cell selection in a grid.
62162  * @constructor
62163  * @param {Object} config The object containing the configuration of this model.
62164  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62165  */
62166 Roo.grid.CellSelectionModel = function(config){
62167     Roo.apply(this, config);
62168
62169     this.selection = null;
62170
62171     this.addEvents({
62172         /**
62173              * @event beforerowselect
62174              * Fires before a cell is selected.
62175              * @param {SelectionModel} this
62176              * @param {Number} rowIndex The selected row index
62177              * @param {Number} colIndex The selected cell index
62178              */
62179             "beforecellselect" : true,
62180         /**
62181              * @event cellselect
62182              * Fires when a cell is selected.
62183              * @param {SelectionModel} this
62184              * @param {Number} rowIndex The selected row index
62185              * @param {Number} colIndex The selected cell index
62186              */
62187             "cellselect" : true,
62188         /**
62189              * @event selectionchange
62190              * Fires when the active selection changes.
62191              * @param {SelectionModel} this
62192              * @param {Object} selection null for no selection or an object (o) with two properties
62193                 <ul>
62194                 <li>o.record: the record object for the row the selection is in</li>
62195                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62196                 </ul>
62197              */
62198             "selectionchange" : true,
62199         /**
62200              * @event tabend
62201              * Fires when the tab (or enter) was pressed on the last editable cell
62202              * You can use this to trigger add new row.
62203              * @param {SelectionModel} this
62204              */
62205             "tabend" : true,
62206          /**
62207              * @event beforeeditnext
62208              * Fires before the next editable sell is made active
62209              * You can use this to skip to another cell or fire the tabend
62210              *    if you set cell to false
62211              * @param {Object} eventdata object : { cell : [ row, col ] } 
62212              */
62213             "beforeeditnext" : true
62214     });
62215     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62216 };
62217
62218 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62219     
62220     enter_is_tab: false,
62221
62222     /** @ignore */
62223     initEvents : function(){
62224         this.grid.on("mousedown", this.handleMouseDown, this);
62225         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62226         var view = this.grid.view;
62227         view.on("refresh", this.onViewChange, this);
62228         view.on("rowupdated", this.onRowUpdated, this);
62229         view.on("beforerowremoved", this.clearSelections, this);
62230         view.on("beforerowsinserted", this.clearSelections, this);
62231         if(this.grid.isEditor){
62232             this.grid.on("beforeedit", this.beforeEdit,  this);
62233         }
62234     },
62235
62236         //private
62237     beforeEdit : function(e){
62238         this.select(e.row, e.column, false, true, e.record);
62239     },
62240
62241         //private
62242     onRowUpdated : function(v, index, r){
62243         if(this.selection && this.selection.record == r){
62244             v.onCellSelect(index, this.selection.cell[1]);
62245         }
62246     },
62247
62248         //private
62249     onViewChange : function(){
62250         this.clearSelections(true);
62251     },
62252
62253         /**
62254          * Returns the currently selected cell,.
62255          * @return {Array} The selected cell (row, column) or null if none selected.
62256          */
62257     getSelectedCell : function(){
62258         return this.selection ? this.selection.cell : null;
62259     },
62260
62261     /**
62262      * Clears all selections.
62263      * @param {Boolean} true to prevent the gridview from being notified about the change.
62264      */
62265     clearSelections : function(preventNotify){
62266         var s = this.selection;
62267         if(s){
62268             if(preventNotify !== true){
62269                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62270             }
62271             this.selection = null;
62272             this.fireEvent("selectionchange", this, null);
62273         }
62274     },
62275
62276     /**
62277      * Returns true if there is a selection.
62278      * @return {Boolean}
62279      */
62280     hasSelection : function(){
62281         return this.selection ? true : false;
62282     },
62283
62284     /** @ignore */
62285     handleMouseDown : function(e, t){
62286         var v = this.grid.getView();
62287         if(this.isLocked()){
62288             return;
62289         };
62290         var row = v.findRowIndex(t);
62291         var cell = v.findCellIndex(t);
62292         if(row !== false && cell !== false){
62293             this.select(row, cell);
62294         }
62295     },
62296
62297     /**
62298      * Selects a cell.
62299      * @param {Number} rowIndex
62300      * @param {Number} collIndex
62301      */
62302     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62303         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62304             this.clearSelections();
62305             r = r || this.grid.dataSource.getAt(rowIndex);
62306             this.selection = {
62307                 record : r,
62308                 cell : [rowIndex, colIndex]
62309             };
62310             if(!preventViewNotify){
62311                 var v = this.grid.getView();
62312                 v.onCellSelect(rowIndex, colIndex);
62313                 if(preventFocus !== true){
62314                     v.focusCell(rowIndex, colIndex);
62315                 }
62316             }
62317             this.fireEvent("cellselect", this, rowIndex, colIndex);
62318             this.fireEvent("selectionchange", this, this.selection);
62319         }
62320     },
62321
62322         //private
62323     isSelectable : function(rowIndex, colIndex, cm){
62324         return !cm.isHidden(colIndex);
62325     },
62326
62327     /** @ignore */
62328     handleKeyDown : function(e){
62329         //Roo.log('Cell Sel Model handleKeyDown');
62330         if(!e.isNavKeyPress()){
62331             return;
62332         }
62333         var g = this.grid, s = this.selection;
62334         if(!s){
62335             e.stopEvent();
62336             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62337             if(cell){
62338                 this.select(cell[0], cell[1]);
62339             }
62340             return;
62341         }
62342         var sm = this;
62343         var walk = function(row, col, step){
62344             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62345         };
62346         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62347         var newCell;
62348
62349       
62350
62351         switch(k){
62352             case e.TAB:
62353                 // handled by onEditorKey
62354                 if (g.isEditor && g.editing) {
62355                     return;
62356                 }
62357                 if(e.shiftKey) {
62358                     newCell = walk(r, c-1, -1);
62359                 } else {
62360                     newCell = walk(r, c+1, 1);
62361                 }
62362                 break;
62363             
62364             case e.DOWN:
62365                newCell = walk(r+1, c, 1);
62366                 break;
62367             
62368             case e.UP:
62369                 newCell = walk(r-1, c, -1);
62370                 break;
62371             
62372             case e.RIGHT:
62373                 newCell = walk(r, c+1, 1);
62374                 break;
62375             
62376             case e.LEFT:
62377                 newCell = walk(r, c-1, -1);
62378                 break;
62379             
62380             case e.ENTER:
62381                 
62382                 if(g.isEditor && !g.editing){
62383                    g.startEditing(r, c);
62384                    e.stopEvent();
62385                    return;
62386                 }
62387                 
62388                 
62389              break;
62390         };
62391         if(newCell){
62392             this.select(newCell[0], newCell[1]);
62393             e.stopEvent();
62394             
62395         }
62396     },
62397
62398     acceptsNav : function(row, col, cm){
62399         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62400     },
62401     /**
62402      * Selects a cell.
62403      * @param {Number} field (not used) - as it's normally used as a listener
62404      * @param {Number} e - event - fake it by using
62405      *
62406      * var e = Roo.EventObjectImpl.prototype;
62407      * e.keyCode = e.TAB
62408      *
62409      * 
62410      */
62411     onEditorKey : function(field, e){
62412         
62413         var k = e.getKey(),
62414             newCell,
62415             g = this.grid,
62416             ed = g.activeEditor,
62417             forward = false;
62418         ///Roo.log('onEditorKey' + k);
62419         
62420         
62421         if (this.enter_is_tab && k == e.ENTER) {
62422             k = e.TAB;
62423         }
62424         
62425         if(k == e.TAB){
62426             if(e.shiftKey){
62427                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62428             }else{
62429                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62430                 forward = true;
62431             }
62432             
62433             e.stopEvent();
62434             
62435         } else if(k == e.ENTER &&  !e.ctrlKey){
62436             ed.completeEdit();
62437             e.stopEvent();
62438             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62439         
62440                 } else if(k == e.ESC){
62441             ed.cancelEdit();
62442         }
62443                 
62444         if (newCell) {
62445             var ecall = { cell : newCell, forward : forward };
62446             this.fireEvent('beforeeditnext', ecall );
62447             newCell = ecall.cell;
62448                         forward = ecall.forward;
62449         }
62450                 
62451         if(newCell){
62452             //Roo.log('next cell after edit');
62453             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62454         } else if (forward) {
62455             // tabbed past last
62456             this.fireEvent.defer(100, this, ['tabend',this]);
62457         }
62458     }
62459 });/*
62460  * Based on:
62461  * Ext JS Library 1.1.1
62462  * Copyright(c) 2006-2007, Ext JS, LLC.
62463  *
62464  * Originally Released Under LGPL - original licence link has changed is not relivant.
62465  *
62466  * Fork - LGPL
62467  * <script type="text/javascript">
62468  */
62469  
62470 /**
62471  * @class Roo.grid.EditorGrid
62472  * @extends Roo.grid.Grid
62473  * Class for creating and editable grid.
62474  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62475  * The container MUST have some type of size defined for the grid to fill. The container will be 
62476  * automatically set to position relative if it isn't already.
62477  * @param {Object} dataSource The data model to bind to
62478  * @param {Object} colModel The column model with info about this grid's columns
62479  */
62480 Roo.grid.EditorGrid = function(container, config){
62481     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62482     this.getGridEl().addClass("xedit-grid");
62483
62484     if(!this.selModel){
62485         this.selModel = new Roo.grid.CellSelectionModel();
62486     }
62487
62488     this.activeEditor = null;
62489
62490         this.addEvents({
62491             /**
62492              * @event beforeedit
62493              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62494              * <ul style="padding:5px;padding-left:16px;">
62495              * <li>grid - This grid</li>
62496              * <li>record - The record being edited</li>
62497              * <li>field - The field name being edited</li>
62498              * <li>value - The value for the field being edited.</li>
62499              * <li>row - The grid row index</li>
62500              * <li>column - The grid column index</li>
62501              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62502              * </ul>
62503              * @param {Object} e An edit event (see above for description)
62504              */
62505             "beforeedit" : true,
62506             /**
62507              * @event afteredit
62508              * Fires after a cell is edited. <br />
62509              * <ul style="padding:5px;padding-left:16px;">
62510              * <li>grid - This grid</li>
62511              * <li>record - The record being edited</li>
62512              * <li>field - The field name being edited</li>
62513              * <li>value - The value being set</li>
62514              * <li>originalValue - The original value for the field, before the edit.</li>
62515              * <li>row - The grid row index</li>
62516              * <li>column - The grid column index</li>
62517              * </ul>
62518              * @param {Object} e An edit event (see above for description)
62519              */
62520             "afteredit" : true,
62521             /**
62522              * @event validateedit
62523              * Fires after a cell is edited, but before the value is set in the record. 
62524          * You can use this to modify the value being set in the field, Return false
62525              * to cancel the change. The edit event object has the following properties <br />
62526              * <ul style="padding:5px;padding-left:16px;">
62527          * <li>editor - This editor</li>
62528              * <li>grid - This grid</li>
62529              * <li>record - The record being edited</li>
62530              * <li>field - The field name being edited</li>
62531              * <li>value - The value being set</li>
62532              * <li>originalValue - The original value for the field, before the edit.</li>
62533              * <li>row - The grid row index</li>
62534              * <li>column - The grid column index</li>
62535              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62536              * </ul>
62537              * @param {Object} e An edit event (see above for description)
62538              */
62539             "validateedit" : true
62540         });
62541     this.on("bodyscroll", this.stopEditing,  this);
62542     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62543 };
62544
62545 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62546     /**
62547      * @cfg {Number} clicksToEdit
62548      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62549      */
62550     clicksToEdit: 2,
62551
62552     // private
62553     isEditor : true,
62554     // private
62555     trackMouseOver: false, // causes very odd FF errors
62556
62557     onCellDblClick : function(g, row, col){
62558         this.startEditing(row, col);
62559     },
62560
62561     onEditComplete : function(ed, value, startValue){
62562         this.editing = false;
62563         this.activeEditor = null;
62564         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62565         var r = ed.record;
62566         var field = this.colModel.getDataIndex(ed.col);
62567         var e = {
62568             grid: this,
62569             record: r,
62570             field: field,
62571             originalValue: startValue,
62572             value: value,
62573             row: ed.row,
62574             column: ed.col,
62575             cancel:false,
62576             editor: ed
62577         };
62578         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62579         cell.show();
62580           
62581         if(String(value) !== String(startValue)){
62582             
62583             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62584                 r.set(field, e.value);
62585                 // if we are dealing with a combo box..
62586                 // then we also set the 'name' colum to be the displayField
62587                 if (ed.field.displayField && ed.field.name) {
62588                     r.set(ed.field.name, ed.field.el.dom.value);
62589                 }
62590                 
62591                 delete e.cancel; //?? why!!!
62592                 this.fireEvent("afteredit", e);
62593             }
62594         } else {
62595             this.fireEvent("afteredit", e); // always fire it!
62596         }
62597         this.view.focusCell(ed.row, ed.col);
62598     },
62599
62600     /**
62601      * Starts editing the specified for the specified row/column
62602      * @param {Number} rowIndex
62603      * @param {Number} colIndex
62604      */
62605     startEditing : function(row, col){
62606         this.stopEditing();
62607         if(this.colModel.isCellEditable(col, row)){
62608             this.view.ensureVisible(row, col, true);
62609           
62610             var r = this.dataSource.getAt(row);
62611             var field = this.colModel.getDataIndex(col);
62612             var cell = Roo.get(this.view.getCell(row,col));
62613             var e = {
62614                 grid: this,
62615                 record: r,
62616                 field: field,
62617                 value: r.data[field],
62618                 row: row,
62619                 column: col,
62620                 cancel:false 
62621             };
62622             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62623                 this.editing = true;
62624                 var ed = this.colModel.getCellEditor(col, row);
62625                 
62626                 if (!ed) {
62627                     return;
62628                 }
62629                 if(!ed.rendered){
62630                     ed.render(ed.parentEl || document.body);
62631                 }
62632                 ed.field.reset();
62633                
62634                 cell.hide();
62635                 
62636                 (function(){ // complex but required for focus issues in safari, ie and opera
62637                     ed.row = row;
62638                     ed.col = col;
62639                     ed.record = r;
62640                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62641                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62642                     this.activeEditor = ed;
62643                     var v = r.data[field];
62644                     ed.startEdit(this.view.getCell(row, col), v);
62645                     // combo's with 'displayField and name set
62646                     if (ed.field.displayField && ed.field.name) {
62647                         ed.field.el.dom.value = r.data[ed.field.name];
62648                     }
62649                     
62650                     
62651                 }).defer(50, this);
62652             }
62653         }
62654     },
62655         
62656     /**
62657      * Stops any active editing
62658      */
62659     stopEditing : function(){
62660         if(this.activeEditor){
62661             this.activeEditor.completeEdit();
62662         }
62663         this.activeEditor = null;
62664     },
62665         
62666          /**
62667      * Called to get grid's drag proxy text, by default returns this.ddText.
62668      * @return {String}
62669      */
62670     getDragDropText : function(){
62671         var count = this.selModel.getSelectedCell() ? 1 : 0;
62672         return String.format(this.ddText, count, count == 1 ? '' : 's');
62673     }
62674         
62675 });/*
62676  * Based on:
62677  * Ext JS Library 1.1.1
62678  * Copyright(c) 2006-2007, Ext JS, LLC.
62679  *
62680  * Originally Released Under LGPL - original licence link has changed is not relivant.
62681  *
62682  * Fork - LGPL
62683  * <script type="text/javascript">
62684  */
62685
62686 // private - not really -- you end up using it !
62687 // This is a support class used internally by the Grid components
62688
62689 /**
62690  * @class Roo.grid.GridEditor
62691  * @extends Roo.Editor
62692  * Class for creating and editable grid elements.
62693  * @param {Object} config any settings (must include field)
62694  */
62695 Roo.grid.GridEditor = function(field, config){
62696     if (!config && field.field) {
62697         config = field;
62698         field = Roo.factory(config.field, Roo.form);
62699     }
62700     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62701     field.monitorTab = false;
62702 };
62703
62704 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62705     
62706     /**
62707      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62708      */
62709     
62710     alignment: "tl-tl",
62711     autoSize: "width",
62712     hideEl : false,
62713     cls: "x-small-editor x-grid-editor",
62714     shim:false,
62715     shadow:"frame"
62716 });/*
62717  * Based on:
62718  * Ext JS Library 1.1.1
62719  * Copyright(c) 2006-2007, Ext JS, LLC.
62720  *
62721  * Originally Released Under LGPL - original licence link has changed is not relivant.
62722  *
62723  * Fork - LGPL
62724  * <script type="text/javascript">
62725  */
62726   
62727
62728   
62729 Roo.grid.PropertyRecord = Roo.data.Record.create([
62730     {name:'name',type:'string'},  'value'
62731 ]);
62732
62733
62734 Roo.grid.PropertyStore = function(grid, source){
62735     this.grid = grid;
62736     this.store = new Roo.data.Store({
62737         recordType : Roo.grid.PropertyRecord
62738     });
62739     this.store.on('update', this.onUpdate,  this);
62740     if(source){
62741         this.setSource(source);
62742     }
62743     Roo.grid.PropertyStore.superclass.constructor.call(this);
62744 };
62745
62746
62747
62748 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62749     setSource : function(o){
62750         this.source = o;
62751         this.store.removeAll();
62752         var data = [];
62753         for(var k in o){
62754             if(this.isEditableValue(o[k])){
62755                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62756             }
62757         }
62758         this.store.loadRecords({records: data}, {}, true);
62759     },
62760
62761     onUpdate : function(ds, record, type){
62762         if(type == Roo.data.Record.EDIT){
62763             var v = record.data['value'];
62764             var oldValue = record.modified['value'];
62765             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62766                 this.source[record.id] = v;
62767                 record.commit();
62768                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62769             }else{
62770                 record.reject();
62771             }
62772         }
62773     },
62774
62775     getProperty : function(row){
62776        return this.store.getAt(row);
62777     },
62778
62779     isEditableValue: function(val){
62780         if(val && val instanceof Date){
62781             return true;
62782         }else if(typeof val == 'object' || typeof val == 'function'){
62783             return false;
62784         }
62785         return true;
62786     },
62787
62788     setValue : function(prop, value){
62789         this.source[prop] = value;
62790         this.store.getById(prop).set('value', value);
62791     },
62792
62793     getSource : function(){
62794         return this.source;
62795     }
62796 });
62797
62798 Roo.grid.PropertyColumnModel = function(grid, store){
62799     this.grid = grid;
62800     var g = Roo.grid;
62801     g.PropertyColumnModel.superclass.constructor.call(this, [
62802         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62803         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62804     ]);
62805     this.store = store;
62806     this.bselect = Roo.DomHelper.append(document.body, {
62807         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62808             {tag: 'option', value: 'true', html: 'true'},
62809             {tag: 'option', value: 'false', html: 'false'}
62810         ]
62811     });
62812     Roo.id(this.bselect);
62813     var f = Roo.form;
62814     this.editors = {
62815         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62816         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62817         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62818         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62819         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62820     };
62821     this.renderCellDelegate = this.renderCell.createDelegate(this);
62822     this.renderPropDelegate = this.renderProp.createDelegate(this);
62823 };
62824
62825 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62826     
62827     
62828     nameText : 'Name',
62829     valueText : 'Value',
62830     
62831     dateFormat : 'm/j/Y',
62832     
62833     
62834     renderDate : function(dateVal){
62835         return dateVal.dateFormat(this.dateFormat);
62836     },
62837
62838     renderBool : function(bVal){
62839         return bVal ? 'true' : 'false';
62840     },
62841
62842     isCellEditable : function(colIndex, rowIndex){
62843         return colIndex == 1;
62844     },
62845
62846     getRenderer : function(col){
62847         return col == 1 ?
62848             this.renderCellDelegate : this.renderPropDelegate;
62849     },
62850
62851     renderProp : function(v){
62852         return this.getPropertyName(v);
62853     },
62854
62855     renderCell : function(val){
62856         var rv = val;
62857         if(val instanceof Date){
62858             rv = this.renderDate(val);
62859         }else if(typeof val == 'boolean'){
62860             rv = this.renderBool(val);
62861         }
62862         return Roo.util.Format.htmlEncode(rv);
62863     },
62864
62865     getPropertyName : function(name){
62866         var pn = this.grid.propertyNames;
62867         return pn && pn[name] ? pn[name] : name;
62868     },
62869
62870     getCellEditor : function(colIndex, rowIndex){
62871         var p = this.store.getProperty(rowIndex);
62872         var n = p.data['name'], val = p.data['value'];
62873         
62874         if(typeof(this.grid.customEditors[n]) == 'string'){
62875             return this.editors[this.grid.customEditors[n]];
62876         }
62877         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62878             return this.grid.customEditors[n];
62879         }
62880         if(val instanceof Date){
62881             return this.editors['date'];
62882         }else if(typeof val == 'number'){
62883             return this.editors['number'];
62884         }else if(typeof val == 'boolean'){
62885             return this.editors['boolean'];
62886         }else{
62887             return this.editors['string'];
62888         }
62889     }
62890 });
62891
62892 /**
62893  * @class Roo.grid.PropertyGrid
62894  * @extends Roo.grid.EditorGrid
62895  * This class represents the  interface of a component based property grid control.
62896  * <br><br>Usage:<pre><code>
62897  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62898       
62899  });
62900  // set any options
62901  grid.render();
62902  * </code></pre>
62903   
62904  * @constructor
62905  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62906  * The container MUST have some type of size defined for the grid to fill. The container will be
62907  * automatically set to position relative if it isn't already.
62908  * @param {Object} config A config object that sets properties on this grid.
62909  */
62910 Roo.grid.PropertyGrid = function(container, config){
62911     config = config || {};
62912     var store = new Roo.grid.PropertyStore(this);
62913     this.store = store;
62914     var cm = new Roo.grid.PropertyColumnModel(this, store);
62915     store.store.sort('name', 'ASC');
62916     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62917         ds: store.store,
62918         cm: cm,
62919         enableColLock:false,
62920         enableColumnMove:false,
62921         stripeRows:false,
62922         trackMouseOver: false,
62923         clicksToEdit:1
62924     }, config));
62925     this.getGridEl().addClass('x-props-grid');
62926     this.lastEditRow = null;
62927     this.on('columnresize', this.onColumnResize, this);
62928     this.addEvents({
62929          /**
62930              * @event beforepropertychange
62931              * Fires before a property changes (return false to stop?)
62932              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62933              * @param {String} id Record Id
62934              * @param {String} newval New Value
62935          * @param {String} oldval Old Value
62936              */
62937         "beforepropertychange": true,
62938         /**
62939              * @event propertychange
62940              * Fires after a property changes
62941              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62942              * @param {String} id Record Id
62943              * @param {String} newval New Value
62944          * @param {String} oldval Old Value
62945              */
62946         "propertychange": true
62947     });
62948     this.customEditors = this.customEditors || {};
62949 };
62950 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62951     
62952      /**
62953      * @cfg {Object} customEditors map of colnames=> custom editors.
62954      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62955      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62956      * false disables editing of the field.
62957          */
62958     
62959       /**
62960      * @cfg {Object} propertyNames map of property Names to their displayed value
62961          */
62962     
62963     render : function(){
62964         Roo.grid.PropertyGrid.superclass.render.call(this);
62965         this.autoSize.defer(100, this);
62966     },
62967
62968     autoSize : function(){
62969         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62970         if(this.view){
62971             this.view.fitColumns();
62972         }
62973     },
62974
62975     onColumnResize : function(){
62976         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62977         this.autoSize();
62978     },
62979     /**
62980      * Sets the data for the Grid
62981      * accepts a Key => Value object of all the elements avaiable.
62982      * @param {Object} data  to appear in grid.
62983      */
62984     setSource : function(source){
62985         this.store.setSource(source);
62986         //this.autoSize();
62987     },
62988     /**
62989      * Gets all the data from the grid.
62990      * @return {Object} data  data stored in grid
62991      */
62992     getSource : function(){
62993         return this.store.getSource();
62994     }
62995 });/*
62996   
62997  * Licence LGPL
62998  
62999  */
63000  
63001 /**
63002  * @class Roo.grid.Calendar
63003  * @extends Roo.grid.Grid
63004  * This class extends the Grid to provide a calendar widget
63005  * <br><br>Usage:<pre><code>
63006  var grid = new Roo.grid.Calendar("my-container-id", {
63007      ds: myDataStore,
63008      cm: myColModel,
63009      selModel: mySelectionModel,
63010      autoSizeColumns: true,
63011      monitorWindowResize: false,
63012      trackMouseOver: true
63013      eventstore : real data store..
63014  });
63015  // set any options
63016  grid.render();
63017   
63018   * @constructor
63019  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63020  * The container MUST have some type of size defined for the grid to fill. The container will be
63021  * automatically set to position relative if it isn't already.
63022  * @param {Object} config A config object that sets properties on this grid.
63023  */
63024 Roo.grid.Calendar = function(container, config){
63025         // initialize the container
63026         this.container = Roo.get(container);
63027         this.container.update("");
63028         this.container.setStyle("overflow", "hidden");
63029     this.container.addClass('x-grid-container');
63030
63031     this.id = this.container.id;
63032
63033     Roo.apply(this, config);
63034     // check and correct shorthanded configs
63035     
63036     var rows = [];
63037     var d =1;
63038     for (var r = 0;r < 6;r++) {
63039         
63040         rows[r]=[];
63041         for (var c =0;c < 7;c++) {
63042             rows[r][c]= '';
63043         }
63044     }
63045     if (this.eventStore) {
63046         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63047         this.eventStore.on('load',this.onLoad, this);
63048         this.eventStore.on('beforeload',this.clearEvents, this);
63049          
63050     }
63051     
63052     this.dataSource = new Roo.data.Store({
63053             proxy: new Roo.data.MemoryProxy(rows),
63054             reader: new Roo.data.ArrayReader({}, [
63055                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63056     });
63057
63058     this.dataSource.load();
63059     this.ds = this.dataSource;
63060     this.ds.xmodule = this.xmodule || false;
63061     
63062     
63063     var cellRender = function(v,x,r)
63064     {
63065         return String.format(
63066             '<div class="fc-day  fc-widget-content"><div>' +
63067                 '<div class="fc-event-container"></div>' +
63068                 '<div class="fc-day-number">{0}</div>'+
63069                 
63070                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63071             '</div></div>', v);
63072     
63073     }
63074     
63075     
63076     this.colModel = new Roo.grid.ColumnModel( [
63077         {
63078             xtype: 'ColumnModel',
63079             xns: Roo.grid,
63080             dataIndex : 'weekday0',
63081             header : 'Sunday',
63082             renderer : cellRender
63083         },
63084         {
63085             xtype: 'ColumnModel',
63086             xns: Roo.grid,
63087             dataIndex : 'weekday1',
63088             header : 'Monday',
63089             renderer : cellRender
63090         },
63091         {
63092             xtype: 'ColumnModel',
63093             xns: Roo.grid,
63094             dataIndex : 'weekday2',
63095             header : 'Tuesday',
63096             renderer : cellRender
63097         },
63098         {
63099             xtype: 'ColumnModel',
63100             xns: Roo.grid,
63101             dataIndex : 'weekday3',
63102             header : 'Wednesday',
63103             renderer : cellRender
63104         },
63105         {
63106             xtype: 'ColumnModel',
63107             xns: Roo.grid,
63108             dataIndex : 'weekday4',
63109             header : 'Thursday',
63110             renderer : cellRender
63111         },
63112         {
63113             xtype: 'ColumnModel',
63114             xns: Roo.grid,
63115             dataIndex : 'weekday5',
63116             header : 'Friday',
63117             renderer : cellRender
63118         },
63119         {
63120             xtype: 'ColumnModel',
63121             xns: Roo.grid,
63122             dataIndex : 'weekday6',
63123             header : 'Saturday',
63124             renderer : cellRender
63125         }
63126     ]);
63127     this.cm = this.colModel;
63128     this.cm.xmodule = this.xmodule || false;
63129  
63130         
63131           
63132     //this.selModel = new Roo.grid.CellSelectionModel();
63133     //this.sm = this.selModel;
63134     //this.selModel.init(this);
63135     
63136     
63137     if(this.width){
63138         this.container.setWidth(this.width);
63139     }
63140
63141     if(this.height){
63142         this.container.setHeight(this.height);
63143     }
63144     /** @private */
63145         this.addEvents({
63146         // raw events
63147         /**
63148          * @event click
63149          * The raw click event for the entire grid.
63150          * @param {Roo.EventObject} e
63151          */
63152         "click" : true,
63153         /**
63154          * @event dblclick
63155          * The raw dblclick event for the entire grid.
63156          * @param {Roo.EventObject} e
63157          */
63158         "dblclick" : true,
63159         /**
63160          * @event contextmenu
63161          * The raw contextmenu event for the entire grid.
63162          * @param {Roo.EventObject} e
63163          */
63164         "contextmenu" : true,
63165         /**
63166          * @event mousedown
63167          * The raw mousedown event for the entire grid.
63168          * @param {Roo.EventObject} e
63169          */
63170         "mousedown" : true,
63171         /**
63172          * @event mouseup
63173          * The raw mouseup event for the entire grid.
63174          * @param {Roo.EventObject} e
63175          */
63176         "mouseup" : true,
63177         /**
63178          * @event mouseover
63179          * The raw mouseover event for the entire grid.
63180          * @param {Roo.EventObject} e
63181          */
63182         "mouseover" : true,
63183         /**
63184          * @event mouseout
63185          * The raw mouseout event for the entire grid.
63186          * @param {Roo.EventObject} e
63187          */
63188         "mouseout" : true,
63189         /**
63190          * @event keypress
63191          * The raw keypress event for the entire grid.
63192          * @param {Roo.EventObject} e
63193          */
63194         "keypress" : true,
63195         /**
63196          * @event keydown
63197          * The raw keydown event for the entire grid.
63198          * @param {Roo.EventObject} e
63199          */
63200         "keydown" : true,
63201
63202         // custom events
63203
63204         /**
63205          * @event cellclick
63206          * Fires when a cell is clicked
63207          * @param {Grid} this
63208          * @param {Number} rowIndex
63209          * @param {Number} columnIndex
63210          * @param {Roo.EventObject} e
63211          */
63212         "cellclick" : true,
63213         /**
63214          * @event celldblclick
63215          * Fires when a cell is double clicked
63216          * @param {Grid} this
63217          * @param {Number} rowIndex
63218          * @param {Number} columnIndex
63219          * @param {Roo.EventObject} e
63220          */
63221         "celldblclick" : true,
63222         /**
63223          * @event rowclick
63224          * Fires when a row is clicked
63225          * @param {Grid} this
63226          * @param {Number} rowIndex
63227          * @param {Roo.EventObject} e
63228          */
63229         "rowclick" : true,
63230         /**
63231          * @event rowdblclick
63232          * Fires when a row is double clicked
63233          * @param {Grid} this
63234          * @param {Number} rowIndex
63235          * @param {Roo.EventObject} e
63236          */
63237         "rowdblclick" : true,
63238         /**
63239          * @event headerclick
63240          * Fires when a header is clicked
63241          * @param {Grid} this
63242          * @param {Number} columnIndex
63243          * @param {Roo.EventObject} e
63244          */
63245         "headerclick" : true,
63246         /**
63247          * @event headerdblclick
63248          * Fires when a header cell is double clicked
63249          * @param {Grid} this
63250          * @param {Number} columnIndex
63251          * @param {Roo.EventObject} e
63252          */
63253         "headerdblclick" : true,
63254         /**
63255          * @event rowcontextmenu
63256          * Fires when a row is right clicked
63257          * @param {Grid} this
63258          * @param {Number} rowIndex
63259          * @param {Roo.EventObject} e
63260          */
63261         "rowcontextmenu" : true,
63262         /**
63263          * @event cellcontextmenu
63264          * Fires when a cell is right clicked
63265          * @param {Grid} this
63266          * @param {Number} rowIndex
63267          * @param {Number} cellIndex
63268          * @param {Roo.EventObject} e
63269          */
63270          "cellcontextmenu" : true,
63271         /**
63272          * @event headercontextmenu
63273          * Fires when a header is right clicked
63274          * @param {Grid} this
63275          * @param {Number} columnIndex
63276          * @param {Roo.EventObject} e
63277          */
63278         "headercontextmenu" : true,
63279         /**
63280          * @event bodyscroll
63281          * Fires when the body element is scrolled
63282          * @param {Number} scrollLeft
63283          * @param {Number} scrollTop
63284          */
63285         "bodyscroll" : true,
63286         /**
63287          * @event columnresize
63288          * Fires when the user resizes a column
63289          * @param {Number} columnIndex
63290          * @param {Number} newSize
63291          */
63292         "columnresize" : true,
63293         /**
63294          * @event columnmove
63295          * Fires when the user moves a column
63296          * @param {Number} oldIndex
63297          * @param {Number} newIndex
63298          */
63299         "columnmove" : true,
63300         /**
63301          * @event startdrag
63302          * Fires when row(s) start being dragged
63303          * @param {Grid} this
63304          * @param {Roo.GridDD} dd The drag drop object
63305          * @param {event} e The raw browser event
63306          */
63307         "startdrag" : true,
63308         /**
63309          * @event enddrag
63310          * Fires when a drag operation is complete
63311          * @param {Grid} this
63312          * @param {Roo.GridDD} dd The drag drop object
63313          * @param {event} e The raw browser event
63314          */
63315         "enddrag" : true,
63316         /**
63317          * @event dragdrop
63318          * Fires when dragged row(s) are dropped on a valid DD target
63319          * @param {Grid} this
63320          * @param {Roo.GridDD} dd The drag drop object
63321          * @param {String} targetId The target drag drop object
63322          * @param {event} e The raw browser event
63323          */
63324         "dragdrop" : true,
63325         /**
63326          * @event dragover
63327          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63328          * @param {Grid} this
63329          * @param {Roo.GridDD} dd The drag drop object
63330          * @param {String} targetId The target drag drop object
63331          * @param {event} e The raw browser event
63332          */
63333         "dragover" : true,
63334         /**
63335          * @event dragenter
63336          *  Fires when the dragged row(s) first cross another DD target while being dragged
63337          * @param {Grid} this
63338          * @param {Roo.GridDD} dd The drag drop object
63339          * @param {String} targetId The target drag drop object
63340          * @param {event} e The raw browser event
63341          */
63342         "dragenter" : true,
63343         /**
63344          * @event dragout
63345          * Fires when the dragged row(s) leave another DD target while being dragged
63346          * @param {Grid} this
63347          * @param {Roo.GridDD} dd The drag drop object
63348          * @param {String} targetId The target drag drop object
63349          * @param {event} e The raw browser event
63350          */
63351         "dragout" : true,
63352         /**
63353          * @event rowclass
63354          * Fires when a row is rendered, so you can change add a style to it.
63355          * @param {GridView} gridview   The grid view
63356          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63357          */
63358         'rowclass' : true,
63359
63360         /**
63361          * @event render
63362          * Fires when the grid is rendered
63363          * @param {Grid} grid
63364          */
63365         'render' : true,
63366             /**
63367              * @event select
63368              * Fires when a date is selected
63369              * @param {DatePicker} this
63370              * @param {Date} date The selected date
63371              */
63372         'select': true,
63373         /**
63374              * @event monthchange
63375              * Fires when the displayed month changes 
63376              * @param {DatePicker} this
63377              * @param {Date} date The selected month
63378              */
63379         'monthchange': true,
63380         /**
63381              * @event evententer
63382              * Fires when mouse over an event
63383              * @param {Calendar} this
63384              * @param {event} Event
63385              */
63386         'evententer': true,
63387         /**
63388              * @event eventleave
63389              * Fires when the mouse leaves an
63390              * @param {Calendar} this
63391              * @param {event}
63392              */
63393         'eventleave': true,
63394         /**
63395              * @event eventclick
63396              * Fires when the mouse click an
63397              * @param {Calendar} this
63398              * @param {event}
63399              */
63400         'eventclick': true,
63401         /**
63402              * @event eventrender
63403              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63404              * @param {Calendar} this
63405              * @param {data} data to be modified
63406              */
63407         'eventrender': true
63408         
63409     });
63410
63411     Roo.grid.Grid.superclass.constructor.call(this);
63412     this.on('render', function() {
63413         this.view.el.addClass('x-grid-cal'); 
63414         
63415         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63416
63417     },this);
63418     
63419     if (!Roo.grid.Calendar.style) {
63420         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63421             
63422             
63423             '.x-grid-cal .x-grid-col' :  {
63424                 height: 'auto !important',
63425                 'vertical-align': 'top'
63426             },
63427             '.x-grid-cal  .fc-event-hori' : {
63428                 height: '14px'
63429             }
63430              
63431             
63432         }, Roo.id());
63433     }
63434
63435     
63436     
63437 };
63438 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63439     /**
63440      * @cfg {Store} eventStore The store that loads events.
63441      */
63442     eventStore : 25,
63443
63444      
63445     activeDate : false,
63446     startDay : 0,
63447     autoWidth : true,
63448     monitorWindowResize : false,
63449
63450     
63451     resizeColumns : function() {
63452         var col = (this.view.el.getWidth() / 7) - 3;
63453         // loop through cols, and setWidth
63454         for(var i =0 ; i < 7 ; i++){
63455             this.cm.setColumnWidth(i, col);
63456         }
63457     },
63458      setDate :function(date) {
63459         
63460         Roo.log('setDate?');
63461         
63462         this.resizeColumns();
63463         var vd = this.activeDate;
63464         this.activeDate = date;
63465 //        if(vd && this.el){
63466 //            var t = date.getTime();
63467 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63468 //                Roo.log('using add remove');
63469 //                
63470 //                this.fireEvent('monthchange', this, date);
63471 //                
63472 //                this.cells.removeClass("fc-state-highlight");
63473 //                this.cells.each(function(c){
63474 //                   if(c.dateValue == t){
63475 //                       c.addClass("fc-state-highlight");
63476 //                       setTimeout(function(){
63477 //                            try{c.dom.firstChild.focus();}catch(e){}
63478 //                       }, 50);
63479 //                       return false;
63480 //                   }
63481 //                   return true;
63482 //                });
63483 //                return;
63484 //            }
63485 //        }
63486         
63487         var days = date.getDaysInMonth();
63488         
63489         var firstOfMonth = date.getFirstDateOfMonth();
63490         var startingPos = firstOfMonth.getDay()-this.startDay;
63491         
63492         if(startingPos < this.startDay){
63493             startingPos += 7;
63494         }
63495         
63496         var pm = date.add(Date.MONTH, -1);
63497         var prevStart = pm.getDaysInMonth()-startingPos;
63498 //        
63499         
63500         
63501         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63502         
63503         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63504         //this.cells.addClassOnOver('fc-state-hover');
63505         
63506         var cells = this.cells.elements;
63507         var textEls = this.textNodes;
63508         
63509         //Roo.each(cells, function(cell){
63510         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63511         //});
63512         
63513         days += startingPos;
63514
63515         // convert everything to numbers so it's fast
63516         var day = 86400000;
63517         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63518         //Roo.log(d);
63519         //Roo.log(pm);
63520         //Roo.log(prevStart);
63521         
63522         var today = new Date().clearTime().getTime();
63523         var sel = date.clearTime().getTime();
63524         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63525         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63526         var ddMatch = this.disabledDatesRE;
63527         var ddText = this.disabledDatesText;
63528         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63529         var ddaysText = this.disabledDaysText;
63530         var format = this.format;
63531         
63532         var setCellClass = function(cal, cell){
63533             
63534             //Roo.log('set Cell Class');
63535             cell.title = "";
63536             var t = d.getTime();
63537             
63538             //Roo.log(d);
63539             
63540             
63541             cell.dateValue = t;
63542             if(t == today){
63543                 cell.className += " fc-today";
63544                 cell.className += " fc-state-highlight";
63545                 cell.title = cal.todayText;
63546             }
63547             if(t == sel){
63548                 // disable highlight in other month..
63549                 cell.className += " fc-state-highlight";
63550                 
63551             }
63552             // disabling
63553             if(t < min) {
63554                 //cell.className = " fc-state-disabled";
63555                 cell.title = cal.minText;
63556                 return;
63557             }
63558             if(t > max) {
63559                 //cell.className = " fc-state-disabled";
63560                 cell.title = cal.maxText;
63561                 return;
63562             }
63563             if(ddays){
63564                 if(ddays.indexOf(d.getDay()) != -1){
63565                     // cell.title = ddaysText;
63566                    // cell.className = " fc-state-disabled";
63567                 }
63568             }
63569             if(ddMatch && format){
63570                 var fvalue = d.dateFormat(format);
63571                 if(ddMatch.test(fvalue)){
63572                     cell.title = ddText.replace("%0", fvalue);
63573                    cell.className = " fc-state-disabled";
63574                 }
63575             }
63576             
63577             if (!cell.initialClassName) {
63578                 cell.initialClassName = cell.dom.className;
63579             }
63580             
63581             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63582         };
63583
63584         var i = 0;
63585         
63586         for(; i < startingPos; i++) {
63587             cells[i].dayName =  (++prevStart);
63588             Roo.log(textEls[i]);
63589             d.setDate(d.getDate()+1);
63590             
63591             //cells[i].className = "fc-past fc-other-month";
63592             setCellClass(this, cells[i]);
63593         }
63594         
63595         var intDay = 0;
63596         
63597         for(; i < days; i++){
63598             intDay = i - startingPos + 1;
63599             cells[i].dayName =  (intDay);
63600             d.setDate(d.getDate()+1);
63601             
63602             cells[i].className = ''; // "x-date-active";
63603             setCellClass(this, cells[i]);
63604         }
63605         var extraDays = 0;
63606         
63607         for(; i < 42; i++) {
63608             //textEls[i].innerHTML = (++extraDays);
63609             
63610             d.setDate(d.getDate()+1);
63611             cells[i].dayName = (++extraDays);
63612             cells[i].className = "fc-future fc-other-month";
63613             setCellClass(this, cells[i]);
63614         }
63615         
63616         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63617         
63618         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63619         
63620         // this will cause all the cells to mis
63621         var rows= [];
63622         var i =0;
63623         for (var r = 0;r < 6;r++) {
63624             for (var c =0;c < 7;c++) {
63625                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63626             }    
63627         }
63628         
63629         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63630         for(i=0;i<cells.length;i++) {
63631             
63632             this.cells.elements[i].dayName = cells[i].dayName ;
63633             this.cells.elements[i].className = cells[i].className;
63634             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63635             this.cells.elements[i].title = cells[i].title ;
63636             this.cells.elements[i].dateValue = cells[i].dateValue ;
63637         }
63638         
63639         
63640         
63641         
63642         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63643         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63644         
63645         ////if(totalRows != 6){
63646             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63647            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63648        // }
63649         
63650         this.fireEvent('monthchange', this, date);
63651         
63652         
63653     },
63654  /**
63655      * Returns the grid's SelectionModel.
63656      * @return {SelectionModel}
63657      */
63658     getSelectionModel : function(){
63659         if(!this.selModel){
63660             this.selModel = new Roo.grid.CellSelectionModel();
63661         }
63662         return this.selModel;
63663     },
63664
63665     load: function() {
63666         this.eventStore.load()
63667         
63668         
63669         
63670     },
63671     
63672     findCell : function(dt) {
63673         dt = dt.clearTime().getTime();
63674         var ret = false;
63675         this.cells.each(function(c){
63676             //Roo.log("check " +c.dateValue + '?=' + dt);
63677             if(c.dateValue == dt){
63678                 ret = c;
63679                 return false;
63680             }
63681             return true;
63682         });
63683         
63684         return ret;
63685     },
63686     
63687     findCells : function(rec) {
63688         var s = rec.data.start_dt.clone().clearTime().getTime();
63689        // Roo.log(s);
63690         var e= rec.data.end_dt.clone().clearTime().getTime();
63691        // Roo.log(e);
63692         var ret = [];
63693         this.cells.each(function(c){
63694              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63695             
63696             if(c.dateValue > e){
63697                 return ;
63698             }
63699             if(c.dateValue < s){
63700                 return ;
63701             }
63702             ret.push(c);
63703         });
63704         
63705         return ret;    
63706     },
63707     
63708     findBestRow: function(cells)
63709     {
63710         var ret = 0;
63711         
63712         for (var i =0 ; i < cells.length;i++) {
63713             ret  = Math.max(cells[i].rows || 0,ret);
63714         }
63715         return ret;
63716         
63717     },
63718     
63719     
63720     addItem : function(rec)
63721     {
63722         // look for vertical location slot in
63723         var cells = this.findCells(rec);
63724         
63725         rec.row = this.findBestRow(cells);
63726         
63727         // work out the location.
63728         
63729         var crow = false;
63730         var rows = [];
63731         for(var i =0; i < cells.length; i++) {
63732             if (!crow) {
63733                 crow = {
63734                     start : cells[i],
63735                     end :  cells[i]
63736                 };
63737                 continue;
63738             }
63739             if (crow.start.getY() == cells[i].getY()) {
63740                 // on same row.
63741                 crow.end = cells[i];
63742                 continue;
63743             }
63744             // different row.
63745             rows.push(crow);
63746             crow = {
63747                 start: cells[i],
63748                 end : cells[i]
63749             };
63750             
63751         }
63752         
63753         rows.push(crow);
63754         rec.els = [];
63755         rec.rows = rows;
63756         rec.cells = cells;
63757         for (var i = 0; i < cells.length;i++) {
63758             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63759             
63760         }
63761         
63762         
63763     },
63764     
63765     clearEvents: function() {
63766         
63767         if (!this.eventStore.getCount()) {
63768             return;
63769         }
63770         // reset number of rows in cells.
63771         Roo.each(this.cells.elements, function(c){
63772             c.rows = 0;
63773         });
63774         
63775         this.eventStore.each(function(e) {
63776             this.clearEvent(e);
63777         },this);
63778         
63779     },
63780     
63781     clearEvent : function(ev)
63782     {
63783         if (ev.els) {
63784             Roo.each(ev.els, function(el) {
63785                 el.un('mouseenter' ,this.onEventEnter, this);
63786                 el.un('mouseleave' ,this.onEventLeave, this);
63787                 el.remove();
63788             },this);
63789             ev.els = [];
63790         }
63791     },
63792     
63793     
63794     renderEvent : function(ev,ctr) {
63795         if (!ctr) {
63796              ctr = this.view.el.select('.fc-event-container',true).first();
63797         }
63798         
63799          
63800         this.clearEvent(ev);
63801             //code
63802        
63803         
63804         
63805         ev.els = [];
63806         var cells = ev.cells;
63807         var rows = ev.rows;
63808         this.fireEvent('eventrender', this, ev);
63809         
63810         for(var i =0; i < rows.length; i++) {
63811             
63812             cls = '';
63813             if (i == 0) {
63814                 cls += ' fc-event-start';
63815             }
63816             if ((i+1) == rows.length) {
63817                 cls += ' fc-event-end';
63818             }
63819             
63820             //Roo.log(ev.data);
63821             // how many rows should it span..
63822             var cg = this.eventTmpl.append(ctr,Roo.apply({
63823                 fccls : cls
63824                 
63825             }, ev.data) , true);
63826             
63827             
63828             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63829             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63830             cg.on('click', this.onEventClick, this, ev);
63831             
63832             ev.els.push(cg);
63833             
63834             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63835             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63836             //Roo.log(cg);
63837              
63838             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63839             cg.setWidth(ebox.right - sbox.x -2);
63840         }
63841     },
63842     
63843     renderEvents: function()
63844     {   
63845         // first make sure there is enough space..
63846         
63847         if (!this.eventTmpl) {
63848             this.eventTmpl = new Roo.Template(
63849                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63850                     '<div class="fc-event-inner">' +
63851                         '<span class="fc-event-time">{time}</span>' +
63852                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63853                     '</div>' +
63854                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63855                 '</div>'
63856             );
63857                 
63858         }
63859                
63860         
63861         
63862         this.cells.each(function(c) {
63863             //Roo.log(c.select('.fc-day-content div',true).first());
63864             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63865         });
63866         
63867         var ctr = this.view.el.select('.fc-event-container',true).first();
63868         
63869         var cls;
63870         this.eventStore.each(function(ev){
63871             
63872             this.renderEvent(ev);
63873              
63874              
63875         }, this);
63876         this.view.layout();
63877         
63878     },
63879     
63880     onEventEnter: function (e, el,event,d) {
63881         this.fireEvent('evententer', this, el, event);
63882     },
63883     
63884     onEventLeave: function (e, el,event,d) {
63885         this.fireEvent('eventleave', this, el, event);
63886     },
63887     
63888     onEventClick: function (e, el,event,d) {
63889         this.fireEvent('eventclick', this, el, event);
63890     },
63891     
63892     onMonthChange: function () {
63893         this.store.load();
63894     },
63895     
63896     onLoad: function () {
63897         
63898         //Roo.log('calendar onload');
63899 //         
63900         if(this.eventStore.getCount() > 0){
63901             
63902            
63903             
63904             this.eventStore.each(function(d){
63905                 
63906                 
63907                 // FIXME..
63908                 var add =   d.data;
63909                 if (typeof(add.end_dt) == 'undefined')  {
63910                     Roo.log("Missing End time in calendar data: ");
63911                     Roo.log(d);
63912                     return;
63913                 }
63914                 if (typeof(add.start_dt) == 'undefined')  {
63915                     Roo.log("Missing Start time in calendar data: ");
63916                     Roo.log(d);
63917                     return;
63918                 }
63919                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63920                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63921                 add.id = add.id || d.id;
63922                 add.title = add.title || '??';
63923                 
63924                 this.addItem(d);
63925                 
63926              
63927             },this);
63928         }
63929         
63930         this.renderEvents();
63931     }
63932     
63933
63934 });
63935 /*
63936  grid : {
63937                 xtype: 'Grid',
63938                 xns: Roo.grid,
63939                 listeners : {
63940                     render : function ()
63941                     {
63942                         _this.grid = this;
63943                         
63944                         if (!this.view.el.hasClass('course-timesheet')) {
63945                             this.view.el.addClass('course-timesheet');
63946                         }
63947                         if (this.tsStyle) {
63948                             this.ds.load({});
63949                             return; 
63950                         }
63951                         Roo.log('width');
63952                         Roo.log(_this.grid.view.el.getWidth());
63953                         
63954                         
63955                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63956                             '.course-timesheet .x-grid-row' : {
63957                                 height: '80px'
63958                             },
63959                             '.x-grid-row td' : {
63960                                 'vertical-align' : 0
63961                             },
63962                             '.course-edit-link' : {
63963                                 'color' : 'blue',
63964                                 'text-overflow' : 'ellipsis',
63965                                 'overflow' : 'hidden',
63966                                 'white-space' : 'nowrap',
63967                                 'cursor' : 'pointer'
63968                             },
63969                             '.sub-link' : {
63970                                 'color' : 'green'
63971                             },
63972                             '.de-act-sup-link' : {
63973                                 'color' : 'purple',
63974                                 'text-decoration' : 'line-through'
63975                             },
63976                             '.de-act-link' : {
63977                                 'color' : 'red',
63978                                 'text-decoration' : 'line-through'
63979                             },
63980                             '.course-timesheet .course-highlight' : {
63981                                 'border-top-style': 'dashed !important',
63982                                 'border-bottom-bottom': 'dashed !important'
63983                             },
63984                             '.course-timesheet .course-item' : {
63985                                 'font-family'   : 'tahoma, arial, helvetica',
63986                                 'font-size'     : '11px',
63987                                 'overflow'      : 'hidden',
63988                                 'padding-left'  : '10px',
63989                                 'padding-right' : '10px',
63990                                 'padding-top' : '10px' 
63991                             }
63992                             
63993                         }, Roo.id());
63994                                 this.ds.load({});
63995                     }
63996                 },
63997                 autoWidth : true,
63998                 monitorWindowResize : false,
63999                 cellrenderer : function(v,x,r)
64000                 {
64001                     return v;
64002                 },
64003                 sm : {
64004                     xtype: 'CellSelectionModel',
64005                     xns: Roo.grid
64006                 },
64007                 dataSource : {
64008                     xtype: 'Store',
64009                     xns: Roo.data,
64010                     listeners : {
64011                         beforeload : function (_self, options)
64012                         {
64013                             options.params = options.params || {};
64014                             options.params._month = _this.monthField.getValue();
64015                             options.params.limit = 9999;
64016                             options.params['sort'] = 'when_dt';    
64017                             options.params['dir'] = 'ASC';    
64018                             this.proxy.loadResponse = this.loadResponse;
64019                             Roo.log("load?");
64020                             //this.addColumns();
64021                         },
64022                         load : function (_self, records, options)
64023                         {
64024                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64025                                 // if you click on the translation.. you can edit it...
64026                                 var el = Roo.get(this);
64027                                 var id = el.dom.getAttribute('data-id');
64028                                 var d = el.dom.getAttribute('data-date');
64029                                 var t = el.dom.getAttribute('data-time');
64030                                 //var id = this.child('span').dom.textContent;
64031                                 
64032                                 //Roo.log(this);
64033                                 Pman.Dialog.CourseCalendar.show({
64034                                     id : id,
64035                                     when_d : d,
64036                                     when_t : t,
64037                                     productitem_active : id ? 1 : 0
64038                                 }, function() {
64039                                     _this.grid.ds.load({});
64040                                 });
64041                            
64042                            });
64043                            
64044                            _this.panel.fireEvent('resize', [ '', '' ]);
64045                         }
64046                     },
64047                     loadResponse : function(o, success, response){
64048                             // this is overridden on before load..
64049                             
64050                             Roo.log("our code?");       
64051                             //Roo.log(success);
64052                             //Roo.log(response)
64053                             delete this.activeRequest;
64054                             if(!success){
64055                                 this.fireEvent("loadexception", this, o, response);
64056                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64057                                 return;
64058                             }
64059                             var result;
64060                             try {
64061                                 result = o.reader.read(response);
64062                             }catch(e){
64063                                 Roo.log("load exception?");
64064                                 this.fireEvent("loadexception", this, o, response, e);
64065                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64066                                 return;
64067                             }
64068                             Roo.log("ready...");        
64069                             // loop through result.records;
64070                             // and set this.tdate[date] = [] << array of records..
64071                             _this.tdata  = {};
64072                             Roo.each(result.records, function(r){
64073                                 //Roo.log(r.data);
64074                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64075                                     _this.tdata[r.data.when_dt.format('j')] = [];
64076                                 }
64077                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64078                             });
64079                             
64080                             //Roo.log(_this.tdata);
64081                             
64082                             result.records = [];
64083                             result.totalRecords = 6;
64084                     
64085                             // let's generate some duumy records for the rows.
64086                             //var st = _this.dateField.getValue();
64087                             
64088                             // work out monday..
64089                             //st = st.add(Date.DAY, -1 * st.format('w'));
64090                             
64091                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64092                             
64093                             var firstOfMonth = date.getFirstDayOfMonth();
64094                             var days = date.getDaysInMonth();
64095                             var d = 1;
64096                             var firstAdded = false;
64097                             for (var i = 0; i < result.totalRecords ; i++) {
64098                                 //var d= st.add(Date.DAY, i);
64099                                 var row = {};
64100                                 var added = 0;
64101                                 for(var w = 0 ; w < 7 ; w++){
64102                                     if(!firstAdded && firstOfMonth != w){
64103                                         continue;
64104                                     }
64105                                     if(d > days){
64106                                         continue;
64107                                     }
64108                                     firstAdded = true;
64109                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64110                                     row['weekday'+w] = String.format(
64111                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64112                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64113                                                     d,
64114                                                     date.format('Y-m-')+dd
64115                                                 );
64116                                     added++;
64117                                     if(typeof(_this.tdata[d]) != 'undefined'){
64118                                         Roo.each(_this.tdata[d], function(r){
64119                                             var is_sub = '';
64120                                             var deactive = '';
64121                                             var id = r.id;
64122                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64123                                             if(r.parent_id*1>0){
64124                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64125                                                 id = r.parent_id;
64126                                             }
64127                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64128                                                 deactive = 'de-act-link';
64129                                             }
64130                                             
64131                                             row['weekday'+w] += String.format(
64132                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64133                                                     id, //0
64134                                                     r.product_id_name, //1
64135                                                     r.when_dt.format('h:ia'), //2
64136                                                     is_sub, //3
64137                                                     deactive, //4
64138                                                     desc // 5
64139                                             );
64140                                         });
64141                                     }
64142                                     d++;
64143                                 }
64144                                 
64145                                 // only do this if something added..
64146                                 if(added > 0){ 
64147                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64148                                 }
64149                                 
64150                                 
64151                                 // push it twice. (second one with an hour..
64152                                 
64153                             }
64154                             //Roo.log(result);
64155                             this.fireEvent("load", this, o, o.request.arg);
64156                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64157                         },
64158                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64159                     proxy : {
64160                         xtype: 'HttpProxy',
64161                         xns: Roo.data,
64162                         method : 'GET',
64163                         url : baseURL + '/Roo/Shop_course.php'
64164                     },
64165                     reader : {
64166                         xtype: 'JsonReader',
64167                         xns: Roo.data,
64168                         id : 'id',
64169                         fields : [
64170                             {
64171                                 'name': 'id',
64172                                 'type': 'int'
64173                             },
64174                             {
64175                                 'name': 'when_dt',
64176                                 'type': 'string'
64177                             },
64178                             {
64179                                 'name': 'end_dt',
64180                                 'type': 'string'
64181                             },
64182                             {
64183                                 'name': 'parent_id',
64184                                 'type': 'int'
64185                             },
64186                             {
64187                                 'name': 'product_id',
64188                                 'type': 'int'
64189                             },
64190                             {
64191                                 'name': 'productitem_id',
64192                                 'type': 'int'
64193                             },
64194                             {
64195                                 'name': 'guid',
64196                                 'type': 'int'
64197                             }
64198                         ]
64199                     }
64200                 },
64201                 toolbar : {
64202                     xtype: 'Toolbar',
64203                     xns: Roo,
64204                     items : [
64205                         {
64206                             xtype: 'Button',
64207                             xns: Roo.Toolbar,
64208                             listeners : {
64209                                 click : function (_self, e)
64210                                 {
64211                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64212                                     sd.setMonth(sd.getMonth()-1);
64213                                     _this.monthField.setValue(sd.format('Y-m-d'));
64214                                     _this.grid.ds.load({});
64215                                 }
64216                             },
64217                             text : "Back"
64218                         },
64219                         {
64220                             xtype: 'Separator',
64221                             xns: Roo.Toolbar
64222                         },
64223                         {
64224                             xtype: 'MonthField',
64225                             xns: Roo.form,
64226                             listeners : {
64227                                 render : function (_self)
64228                                 {
64229                                     _this.monthField = _self;
64230                                    // _this.monthField.set  today
64231                                 },
64232                                 select : function (combo, date)
64233                                 {
64234                                     _this.grid.ds.load({});
64235                                 }
64236                             },
64237                             value : (function() { return new Date(); })()
64238                         },
64239                         {
64240                             xtype: 'Separator',
64241                             xns: Roo.Toolbar
64242                         },
64243                         {
64244                             xtype: 'TextItem',
64245                             xns: Roo.Toolbar,
64246                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64247                         },
64248                         {
64249                             xtype: 'Fill',
64250                             xns: Roo.Toolbar
64251                         },
64252                         {
64253                             xtype: 'Button',
64254                             xns: Roo.Toolbar,
64255                             listeners : {
64256                                 click : function (_self, e)
64257                                 {
64258                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64259                                     sd.setMonth(sd.getMonth()+1);
64260                                     _this.monthField.setValue(sd.format('Y-m-d'));
64261                                     _this.grid.ds.load({});
64262                                 }
64263                             },
64264                             text : "Next"
64265                         }
64266                     ]
64267                 },
64268                  
64269             }
64270         };
64271         
64272         *//*
64273  * Based on:
64274  * Ext JS Library 1.1.1
64275  * Copyright(c) 2006-2007, Ext JS, LLC.
64276  *
64277  * Originally Released Under LGPL - original licence link has changed is not relivant.
64278  *
64279  * Fork - LGPL
64280  * <script type="text/javascript">
64281  */
64282  
64283 /**
64284  * @class Roo.LoadMask
64285  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64286  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64287  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64288  * element's UpdateManager load indicator and will be destroyed after the initial load.
64289  * @constructor
64290  * Create a new LoadMask
64291  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64292  * @param {Object} config The config object
64293  */
64294 Roo.LoadMask = function(el, config){
64295     this.el = Roo.get(el);
64296     Roo.apply(this, config);
64297     if(this.store){
64298         this.store.on('beforeload', this.onBeforeLoad, this);
64299         this.store.on('load', this.onLoad, this);
64300         this.store.on('loadexception', this.onLoadException, this);
64301         this.removeMask = false;
64302     }else{
64303         var um = this.el.getUpdateManager();
64304         um.showLoadIndicator = false; // disable the default indicator
64305         um.on('beforeupdate', this.onBeforeLoad, this);
64306         um.on('update', this.onLoad, this);
64307         um.on('failure', this.onLoad, this);
64308         this.removeMask = true;
64309     }
64310 };
64311
64312 Roo.LoadMask.prototype = {
64313     /**
64314      * @cfg {Boolean} removeMask
64315      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64316      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64317      */
64318     removeMask : false,
64319     /**
64320      * @cfg {String} msg
64321      * The text to display in a centered loading message box (defaults to 'Loading...')
64322      */
64323     msg : 'Loading...',
64324     /**
64325      * @cfg {String} msgCls
64326      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64327      */
64328     msgCls : 'x-mask-loading',
64329
64330     /**
64331      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64332      * @type Boolean
64333      */
64334     disabled: false,
64335
64336     /**
64337      * Disables the mask to prevent it from being displayed
64338      */
64339     disable : function(){
64340        this.disabled = true;
64341     },
64342
64343     /**
64344      * Enables the mask so that it can be displayed
64345      */
64346     enable : function(){
64347         this.disabled = false;
64348     },
64349     
64350     onLoadException : function()
64351     {
64352         Roo.log(arguments);
64353         
64354         if (typeof(arguments[3]) != 'undefined') {
64355             Roo.MessageBox.alert("Error loading",arguments[3]);
64356         } 
64357         /*
64358         try {
64359             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64360                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64361             }   
64362         } catch(e) {
64363             
64364         }
64365         */
64366     
64367         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64368     },
64369     // private
64370     onLoad : function()
64371     {
64372         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64373     },
64374
64375     // private
64376     onBeforeLoad : function(){
64377         if(!this.disabled){
64378             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64379         }
64380     },
64381
64382     // private
64383     destroy : function(){
64384         if(this.store){
64385             this.store.un('beforeload', this.onBeforeLoad, this);
64386             this.store.un('load', this.onLoad, this);
64387             this.store.un('loadexception', this.onLoadException, this);
64388         }else{
64389             var um = this.el.getUpdateManager();
64390             um.un('beforeupdate', this.onBeforeLoad, this);
64391             um.un('update', this.onLoad, this);
64392             um.un('failure', this.onLoad, this);
64393         }
64394     }
64395 };/*
64396  * Based on:
64397  * Ext JS Library 1.1.1
64398  * Copyright(c) 2006-2007, Ext JS, LLC.
64399  *
64400  * Originally Released Under LGPL - original licence link has changed is not relivant.
64401  *
64402  * Fork - LGPL
64403  * <script type="text/javascript">
64404  */
64405
64406
64407 /**
64408  * @class Roo.XTemplate
64409  * @extends Roo.Template
64410  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64411 <pre><code>
64412 var t = new Roo.XTemplate(
64413         '&lt;select name="{name}"&gt;',
64414                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64415         '&lt;/select&gt;'
64416 );
64417  
64418 // then append, applying the master template values
64419  </code></pre>
64420  *
64421  * Supported features:
64422  *
64423  *  Tags:
64424
64425 <pre><code>
64426       {a_variable} - output encoded.
64427       {a_variable.format:("Y-m-d")} - call a method on the variable
64428       {a_variable:raw} - unencoded output
64429       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64430       {a_variable:this.method_on_template(...)} - call a method on the template object.
64431  
64432 </code></pre>
64433  *  The tpl tag:
64434 <pre><code>
64435         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64436         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64437         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64438         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64439   
64440         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64441         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64442 </code></pre>
64443  *      
64444  */
64445 Roo.XTemplate = function()
64446 {
64447     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64448     if (this.html) {
64449         this.compile();
64450     }
64451 };
64452
64453
64454 Roo.extend(Roo.XTemplate, Roo.Template, {
64455
64456     /**
64457      * The various sub templates
64458      */
64459     tpls : false,
64460     /**
64461      *
64462      * basic tag replacing syntax
64463      * WORD:WORD()
64464      *
64465      * // you can fake an object call by doing this
64466      *  x.t:(test,tesT) 
64467      * 
64468      */
64469     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64470
64471     /**
64472      * compile the template
64473      *
64474      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64475      *
64476      */
64477     compile: function()
64478     {
64479         var s = this.html;
64480      
64481         s = ['<tpl>', s, '</tpl>'].join('');
64482     
64483         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64484             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64485             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64486             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64487             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64488             m,
64489             id     = 0,
64490             tpls   = [];
64491     
64492         while(true == !!(m = s.match(re))){
64493             var forMatch   = m[0].match(nameRe),
64494                 ifMatch   = m[0].match(ifRe),
64495                 execMatch   = m[0].match(execRe),
64496                 namedMatch   = m[0].match(namedRe),
64497                 
64498                 exp  = null, 
64499                 fn   = null,
64500                 exec = null,
64501                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64502                 
64503             if (ifMatch) {
64504                 // if - puts fn into test..
64505                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64506                 if(exp){
64507                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64508                 }
64509             }
64510             
64511             if (execMatch) {
64512                 // exec - calls a function... returns empty if true is  returned.
64513                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64514                 if(exp){
64515                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64516                 }
64517             }
64518             
64519             
64520             if (name) {
64521                 // for = 
64522                 switch(name){
64523                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64524                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64525                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64526                 }
64527             }
64528             var uid = namedMatch ? namedMatch[1] : id;
64529             
64530             
64531             tpls.push({
64532                 id:     namedMatch ? namedMatch[1] : id,
64533                 target: name,
64534                 exec:   exec,
64535                 test:   fn,
64536                 body:   m[1] || ''
64537             });
64538             if (namedMatch) {
64539                 s = s.replace(m[0], '');
64540             } else { 
64541                 s = s.replace(m[0], '{xtpl'+ id + '}');
64542             }
64543             ++id;
64544         }
64545         this.tpls = [];
64546         for(var i = tpls.length-1; i >= 0; --i){
64547             this.compileTpl(tpls[i]);
64548             this.tpls[tpls[i].id] = tpls[i];
64549         }
64550         this.master = tpls[tpls.length-1];
64551         return this;
64552     },
64553     /**
64554      * same as applyTemplate, except it's done to one of the subTemplates
64555      * when using named templates, you can do:
64556      *
64557      * var str = pl.applySubTemplate('your-name', values);
64558      *
64559      * 
64560      * @param {Number} id of the template
64561      * @param {Object} values to apply to template
64562      * @param {Object} parent (normaly the instance of this object)
64563      */
64564     applySubTemplate : function(id, values, parent)
64565     {
64566         
64567         
64568         var t = this.tpls[id];
64569         
64570         
64571         try { 
64572             if(t.test && !t.test.call(this, values, parent)){
64573                 return '';
64574             }
64575         } catch(e) {
64576             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64577             Roo.log(e.toString());
64578             Roo.log(t.test);
64579             return ''
64580         }
64581         try { 
64582             
64583             if(t.exec && t.exec.call(this, values, parent)){
64584                 return '';
64585             }
64586         } catch(e) {
64587             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64588             Roo.log(e.toString());
64589             Roo.log(t.exec);
64590             return ''
64591         }
64592         try {
64593             var vs = t.target ? t.target.call(this, values, parent) : values;
64594             parent = t.target ? values : parent;
64595             if(t.target && vs instanceof Array){
64596                 var buf = [];
64597                 for(var i = 0, len = vs.length; i < len; i++){
64598                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64599                 }
64600                 return buf.join('');
64601             }
64602             return t.compiled.call(this, vs, parent);
64603         } catch (e) {
64604             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64605             Roo.log(e.toString());
64606             Roo.log(t.compiled);
64607             return '';
64608         }
64609     },
64610
64611     compileTpl : function(tpl)
64612     {
64613         var fm = Roo.util.Format;
64614         var useF = this.disableFormats !== true;
64615         var sep = Roo.isGecko ? "+" : ",";
64616         var undef = function(str) {
64617             Roo.log("Property not found :"  + str);
64618             return '';
64619         };
64620         
64621         var fn = function(m, name, format, args)
64622         {
64623             //Roo.log(arguments);
64624             args = args ? args.replace(/\\'/g,"'") : args;
64625             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64626             if (typeof(format) == 'undefined') {
64627                 format= 'htmlEncode';
64628             }
64629             if (format == 'raw' ) {
64630                 format = false;
64631             }
64632             
64633             if(name.substr(0, 4) == 'xtpl'){
64634                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64635             }
64636             
64637             // build an array of options to determine if value is undefined..
64638             
64639             // basically get 'xxxx.yyyy' then do
64640             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64641             //    (function () { Roo.log("Property not found"); return ''; })() :
64642             //    ......
64643             
64644             var udef_ar = [];
64645             var lookfor = '';
64646             Roo.each(name.split('.'), function(st) {
64647                 lookfor += (lookfor.length ? '.': '') + st;
64648                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64649             });
64650             
64651             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64652             
64653             
64654             if(format && useF){
64655                 
64656                 args = args ? ',' + args : "";
64657                  
64658                 if(format.substr(0, 5) != "this."){
64659                     format = "fm." + format + '(';
64660                 }else{
64661                     format = 'this.call("'+ format.substr(5) + '", ';
64662                     args = ", values";
64663                 }
64664                 
64665                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64666             }
64667              
64668             if (args.length) {
64669                 // called with xxyx.yuu:(test,test)
64670                 // change to ()
64671                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64672             }
64673             // raw.. - :raw modifier..
64674             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64675             
64676         };
64677         var body;
64678         // branched to use + in gecko and [].join() in others
64679         if(Roo.isGecko){
64680             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64681                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64682                     "';};};";
64683         }else{
64684             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64685             body.push(tpl.body.replace(/(\r\n|\n)/g,
64686                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64687             body.push("'].join('');};};");
64688             body = body.join('');
64689         }
64690         
64691         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64692        
64693         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64694         eval(body);
64695         
64696         return this;
64697     },
64698
64699     applyTemplate : function(values){
64700         return this.master.compiled.call(this, values, {});
64701         //var s = this.subs;
64702     },
64703
64704     apply : function(){
64705         return this.applyTemplate.apply(this, arguments);
64706     }
64707
64708  });
64709
64710 Roo.XTemplate.from = function(el){
64711     el = Roo.getDom(el);
64712     return new Roo.XTemplate(el.value || el.innerHTML);
64713 };