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         
47472             var nodeRange = node.ownerDocument.createRange();
47473             try {
47474                 nodeRange.selectNode(node);
47475             } catch (e) {
47476                 nodeRange.selectNodeContents(node);
47477             }
47478             //nodeRange.collapse(true);
47479             var s = this.win.getSelection();
47480             s.removeAllRanges();
47481             s.addRange(nodeRange);
47482     },
47483     
47484     getSelectedNode: function() 
47485     {
47486         // this may only work on Gecko!!!
47487         
47488         // should we cache this!!!!
47489         
47490         
47491         
47492          
47493         var range = this.createRange(this.getSelection()).cloneRange();
47494         
47495         if (Roo.isIE) {
47496             var parent = range.parentElement();
47497             while (true) {
47498                 var testRange = range.duplicate();
47499                 testRange.moveToElementText(parent);
47500                 if (testRange.inRange(range)) {
47501                     break;
47502                 }
47503                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47504                     break;
47505                 }
47506                 parent = parent.parentElement;
47507             }
47508             return parent;
47509         }
47510         
47511         // is ancestor a text element.
47512         var ac =  range.commonAncestorContainer;
47513         if (ac.nodeType == 3) {
47514             ac = ac.parentNode;
47515         }
47516         
47517         var ar = ac.childNodes;
47518          
47519         var nodes = [];
47520         var other_nodes = [];
47521         var has_other_nodes = false;
47522         for (var i=0;i<ar.length;i++) {
47523             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47524                 continue;
47525             }
47526             // fullly contained node.
47527             
47528             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47529                 nodes.push(ar[i]);
47530                 continue;
47531             }
47532             
47533             // probably selected..
47534             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47535                 other_nodes.push(ar[i]);
47536                 continue;
47537             }
47538             // outer..
47539             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47540                 continue;
47541             }
47542             
47543             
47544             has_other_nodes = true;
47545         }
47546         if (!nodes.length && other_nodes.length) {
47547             nodes= other_nodes;
47548         }
47549         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47550             return false;
47551         }
47552         
47553         return nodes[0];
47554     },
47555     createRange: function(sel)
47556     {
47557         // this has strange effects when using with 
47558         // top toolbar - not sure if it's a great idea.
47559         //this.editor.contentWindow.focus();
47560         if (typeof sel != "undefined") {
47561             try {
47562                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47563             } catch(e) {
47564                 return this.doc.createRange();
47565             }
47566         } else {
47567             return this.doc.createRange();
47568         }
47569     },
47570     getParentElement: function()
47571     {
47572         
47573         this.assignDocWin();
47574         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47575         
47576         var range = this.createRange(sel);
47577          
47578         try {
47579             var p = range.commonAncestorContainer;
47580             while (p.nodeType == 3) { // text node
47581                 p = p.parentNode;
47582             }
47583             return p;
47584         } catch (e) {
47585             return null;
47586         }
47587     
47588     },
47589     /***
47590      *
47591      * Range intersection.. the hard stuff...
47592      *  '-1' = before
47593      *  '0' = hits..
47594      *  '1' = after.
47595      *         [ -- selected range --- ]
47596      *   [fail]                        [fail]
47597      *
47598      *    basically..
47599      *      if end is before start or  hits it. fail.
47600      *      if start is after end or hits it fail.
47601      *
47602      *   if either hits (but other is outside. - then it's not 
47603      *   
47604      *    
47605      **/
47606     
47607     
47608     // @see http://www.thismuchiknow.co.uk/?p=64.
47609     rangeIntersectsNode : function(range, node)
47610     {
47611         var nodeRange = node.ownerDocument.createRange();
47612         try {
47613             nodeRange.selectNode(node);
47614         } catch (e) {
47615             nodeRange.selectNodeContents(node);
47616         }
47617     
47618         var rangeStartRange = range.cloneRange();
47619         rangeStartRange.collapse(true);
47620     
47621         var rangeEndRange = range.cloneRange();
47622         rangeEndRange.collapse(false);
47623     
47624         var nodeStartRange = nodeRange.cloneRange();
47625         nodeStartRange.collapse(true);
47626     
47627         var nodeEndRange = nodeRange.cloneRange();
47628         nodeEndRange.collapse(false);
47629     
47630         return rangeStartRange.compareBoundaryPoints(
47631                  Range.START_TO_START, nodeEndRange) == -1 &&
47632                rangeEndRange.compareBoundaryPoints(
47633                  Range.START_TO_START, nodeStartRange) == 1;
47634         
47635          
47636     },
47637     rangeCompareNode : function(range, node)
47638     {
47639         var nodeRange = node.ownerDocument.createRange();
47640         try {
47641             nodeRange.selectNode(node);
47642         } catch (e) {
47643             nodeRange.selectNodeContents(node);
47644         }
47645         
47646         
47647         range.collapse(true);
47648     
47649         nodeRange.collapse(true);
47650      
47651         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47652         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47653          
47654         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47655         
47656         var nodeIsBefore   =  ss == 1;
47657         var nodeIsAfter    = ee == -1;
47658         
47659         if (nodeIsBefore && nodeIsAfter) {
47660             return 0; // outer
47661         }
47662         if (!nodeIsBefore && nodeIsAfter) {
47663             return 1; //right trailed.
47664         }
47665         
47666         if (nodeIsBefore && !nodeIsAfter) {
47667             return 2;  // left trailed.
47668         }
47669         // fully contined.
47670         return 3;
47671     },
47672  
47673     cleanWordChars : function(input) {// change the chars to hex code
47674         
47675        var swapCodes  = [ 
47676             [    8211, "&#8211;" ], 
47677             [    8212, "&#8212;" ], 
47678             [    8216,  "'" ],  
47679             [    8217, "'" ],  
47680             [    8220, '"' ],  
47681             [    8221, '"' ],  
47682             [    8226, "*" ],  
47683             [    8230, "..." ]
47684         ]; 
47685         var output = input;
47686         Roo.each(swapCodes, function(sw) { 
47687             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47688             
47689             output = output.replace(swapper, sw[1]);
47690         });
47691         
47692         return output;
47693     },
47694     
47695      
47696     
47697         
47698     
47699     cleanUpChild : function (node)
47700     {
47701         
47702         new Roo.htmleditor.FilterComment({node : node});
47703         new Roo.htmleditor.FilterAttributes({
47704                 node : node,
47705                 attrib_black : this.ablack,
47706                 attrib_clean : this.aclean,
47707                 style_white : this.cwhite,
47708                 style_black : this.cblack
47709         });
47710         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47711         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47712          
47713         
47714     },
47715     
47716     /**
47717      * Clean up MS wordisms...
47718      * @deprecated - use filter directly
47719      */
47720     cleanWord : function(node)
47721     {
47722         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47723         
47724     },
47725    
47726     
47727     /**
47728
47729      * @deprecated - use filters
47730      */
47731     cleanTableWidths : function(node)
47732     {
47733         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47734         
47735  
47736     },
47737     
47738      
47739         
47740     applyBlacklists : function()
47741     {
47742         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47743         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47744         
47745         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47746         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47747         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47748         
47749         this.white = [];
47750         this.black = [];
47751         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47752             if (b.indexOf(tag) > -1) {
47753                 return;
47754             }
47755             this.white.push(tag);
47756             
47757         }, this);
47758         
47759         Roo.each(w, function(tag) {
47760             if (b.indexOf(tag) > -1) {
47761                 return;
47762             }
47763             if (this.white.indexOf(tag) > -1) {
47764                 return;
47765             }
47766             this.white.push(tag);
47767             
47768         }, this);
47769         
47770         
47771         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47772             if (w.indexOf(tag) > -1) {
47773                 return;
47774             }
47775             this.black.push(tag);
47776             
47777         }, this);
47778         
47779         Roo.each(b, function(tag) {
47780             if (w.indexOf(tag) > -1) {
47781                 return;
47782             }
47783             if (this.black.indexOf(tag) > -1) {
47784                 return;
47785             }
47786             this.black.push(tag);
47787             
47788         }, this);
47789         
47790         
47791         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47792         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47793         
47794         this.cwhite = [];
47795         this.cblack = [];
47796         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47797             if (b.indexOf(tag) > -1) {
47798                 return;
47799             }
47800             this.cwhite.push(tag);
47801             
47802         }, this);
47803         
47804         Roo.each(w, function(tag) {
47805             if (b.indexOf(tag) > -1) {
47806                 return;
47807             }
47808             if (this.cwhite.indexOf(tag) > -1) {
47809                 return;
47810             }
47811             this.cwhite.push(tag);
47812             
47813         }, this);
47814         
47815         
47816         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47817             if (w.indexOf(tag) > -1) {
47818                 return;
47819             }
47820             this.cblack.push(tag);
47821             
47822         }, this);
47823         
47824         Roo.each(b, function(tag) {
47825             if (w.indexOf(tag) > -1) {
47826                 return;
47827             }
47828             if (this.cblack.indexOf(tag) > -1) {
47829                 return;
47830             }
47831             this.cblack.push(tag);
47832             
47833         }, this);
47834     },
47835     
47836     setStylesheets : function(stylesheets)
47837     {
47838         if(typeof(stylesheets) == 'string'){
47839             Roo.get(this.iframe.contentDocument.head).createChild({
47840                 tag : 'link',
47841                 rel : 'stylesheet',
47842                 type : 'text/css',
47843                 href : stylesheets
47844             });
47845             
47846             return;
47847         }
47848         var _this = this;
47849      
47850         Roo.each(stylesheets, function(s) {
47851             if(!s.length){
47852                 return;
47853             }
47854             
47855             Roo.get(_this.iframe.contentDocument.head).createChild({
47856                 tag : 'link',
47857                 rel : 'stylesheet',
47858                 type : 'text/css',
47859                 href : s
47860             });
47861         });
47862
47863         
47864     },
47865     
47866     removeStylesheets : function()
47867     {
47868         var _this = this;
47869         
47870         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47871             s.remove();
47872         });
47873     },
47874     
47875     setStyle : function(style)
47876     {
47877         Roo.get(this.iframe.contentDocument.head).createChild({
47878             tag : 'style',
47879             type : 'text/css',
47880             html : style
47881         });
47882
47883         return;
47884     }
47885     
47886     // hide stuff that is not compatible
47887     /**
47888      * @event blur
47889      * @hide
47890      */
47891     /**
47892      * @event change
47893      * @hide
47894      */
47895     /**
47896      * @event focus
47897      * @hide
47898      */
47899     /**
47900      * @event specialkey
47901      * @hide
47902      */
47903     /**
47904      * @cfg {String} fieldClass @hide
47905      */
47906     /**
47907      * @cfg {String} focusClass @hide
47908      */
47909     /**
47910      * @cfg {String} autoCreate @hide
47911      */
47912     /**
47913      * @cfg {String} inputType @hide
47914      */
47915     /**
47916      * @cfg {String} invalidClass @hide
47917      */
47918     /**
47919      * @cfg {String} invalidText @hide
47920      */
47921     /**
47922      * @cfg {String} msgFx @hide
47923      */
47924     /**
47925      * @cfg {String} validateOnBlur @hide
47926      */
47927 });
47928
47929 Roo.HtmlEditorCore.white = [
47930         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47931         
47932        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47933        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47934        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47935        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47936        'TABLE',   'UL',         'XMP', 
47937        
47938        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47939       'THEAD',   'TR', 
47940      
47941       'DIR', 'MENU', 'OL', 'UL', 'DL',
47942        
47943       'EMBED',  'OBJECT'
47944 ];
47945
47946
47947 Roo.HtmlEditorCore.black = [
47948     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47949         'APPLET', // 
47950         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47951         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47952         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47953         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47954         //'FONT' // CLEAN LATER..
47955         'COLGROUP', 'COL'  // messy tables.
47956         
47957 ];
47958 Roo.HtmlEditorCore.clean = [ // ?? needed???
47959      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47960 ];
47961 Roo.HtmlEditorCore.tag_remove = [
47962     'FONT', 'TBODY'  
47963 ];
47964 // attributes..
47965
47966 Roo.HtmlEditorCore.ablack = [
47967     'on'
47968 ];
47969     
47970 Roo.HtmlEditorCore.aclean = [ 
47971     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47972 ];
47973
47974 // protocols..
47975 Roo.HtmlEditorCore.pwhite= [
47976         'http',  'https',  'mailto'
47977 ];
47978
47979 // white listed style attributes.
47980 Roo.HtmlEditorCore.cwhite= [
47981       //  'text-align', /// default is to allow most things..
47982       
47983          
47984 //        'font-size'//??
47985 ];
47986
47987 // black listed style attributes.
47988 Roo.HtmlEditorCore.cblack= [
47989       //  'font-size' -- this can be set by the project 
47990 ];
47991
47992
47993
47994
47995     //<script type="text/javascript">
47996
47997 /*
47998  * Ext JS Library 1.1.1
47999  * Copyright(c) 2006-2007, Ext JS, LLC.
48000  * Licence LGPL
48001  * 
48002  */
48003  
48004  
48005 Roo.form.HtmlEditor = function(config){
48006     
48007     
48008     
48009     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48010     
48011     if (!this.toolbars) {
48012         this.toolbars = [];
48013     }
48014     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48015     
48016     
48017 };
48018
48019 /**
48020  * @class Roo.form.HtmlEditor
48021  * @extends Roo.form.Field
48022  * Provides a lightweight HTML Editor component.
48023  *
48024  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48025  * 
48026  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48027  * supported by this editor.</b><br/><br/>
48028  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48029  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48030  */
48031 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48032     /**
48033      * @cfg {Boolean} clearUp
48034      */
48035     clearUp : true,
48036       /**
48037      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48038      */
48039     toolbars : false,
48040    
48041      /**
48042      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48043      *                        Roo.resizable.
48044      */
48045     resizable : false,
48046      /**
48047      * @cfg {Number} height (in pixels)
48048      */   
48049     height: 300,
48050    /**
48051      * @cfg {Number} width (in pixels)
48052      */   
48053     width: 500,
48054     
48055     /**
48056      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48057      * 
48058      */
48059     stylesheets: false,
48060     
48061     
48062      /**
48063      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48064      * 
48065      */
48066     cblack: false,
48067     /**
48068      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48069      * 
48070      */
48071     cwhite: false,
48072     
48073      /**
48074      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48075      * 
48076      */
48077     black: false,
48078     /**
48079      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48080      * 
48081      */
48082     white: false,
48083     /**
48084      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48085      */
48086     allowComments: false,
48087     /**
48088      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48089      */
48090     
48091     
48092      bodyCls : '',
48093     
48094     // id of frame..
48095     frameId: false,
48096     
48097     // private properties
48098     validationEvent : false,
48099     deferHeight: true,
48100     initialized : false,
48101     activated : false,
48102     
48103     onFocus : Roo.emptyFn,
48104     iframePad:3,
48105     hideMode:'offsets',
48106     
48107     actionMode : 'container', // defaults to hiding it...
48108     
48109     defaultAutoCreate : { // modified by initCompnoent..
48110         tag: "textarea",
48111         style:"width:500px;height:300px;",
48112         autocomplete: "new-password"
48113     },
48114
48115     // private
48116     initComponent : function(){
48117         this.addEvents({
48118             /**
48119              * @event initialize
48120              * Fires when the editor is fully initialized (including the iframe)
48121              * @param {HtmlEditor} this
48122              */
48123             initialize: true,
48124             /**
48125              * @event activate
48126              * Fires when the editor is first receives the focus. Any insertion must wait
48127              * until after this event.
48128              * @param {HtmlEditor} this
48129              */
48130             activate: true,
48131              /**
48132              * @event beforesync
48133              * Fires before the textarea is updated with content from the editor iframe. Return false
48134              * to cancel the sync.
48135              * @param {HtmlEditor} this
48136              * @param {String} html
48137              */
48138             beforesync: true,
48139              /**
48140              * @event beforepush
48141              * Fires before the iframe editor is updated with content from the textarea. Return false
48142              * to cancel the push.
48143              * @param {HtmlEditor} this
48144              * @param {String} html
48145              */
48146             beforepush: true,
48147              /**
48148              * @event sync
48149              * Fires when the textarea is updated with content from the editor iframe.
48150              * @param {HtmlEditor} this
48151              * @param {String} html
48152              */
48153             sync: true,
48154              /**
48155              * @event push
48156              * Fires when the iframe editor is updated with content from the textarea.
48157              * @param {HtmlEditor} this
48158              * @param {String} html
48159              */
48160             push: true,
48161              /**
48162              * @event editmodechange
48163              * Fires when the editor switches edit modes
48164              * @param {HtmlEditor} this
48165              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48166              */
48167             editmodechange: true,
48168             /**
48169              * @event editorevent
48170              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48171              * @param {HtmlEditor} this
48172              */
48173             editorevent: true,
48174             /**
48175              * @event firstfocus
48176              * Fires when on first focus - needed by toolbars..
48177              * @param {HtmlEditor} this
48178              */
48179             firstfocus: true,
48180             /**
48181              * @event autosave
48182              * Auto save the htmlEditor value as a file into Events
48183              * @param {HtmlEditor} this
48184              */
48185             autosave: true,
48186             /**
48187              * @event savedpreview
48188              * preview the saved version of htmlEditor
48189              * @param {HtmlEditor} this
48190              */
48191             savedpreview: true,
48192             
48193             /**
48194             * @event stylesheetsclick
48195             * Fires when press the Sytlesheets button
48196             * @param {Roo.HtmlEditorCore} this
48197             */
48198             stylesheetsclick: true,
48199             /**
48200             * @event paste
48201             * Fires when press user pastes into the editor
48202             * @param {Roo.HtmlEditorCore} this
48203             */
48204             paste: true 
48205         });
48206         this.defaultAutoCreate =  {
48207             tag: "textarea",
48208             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48209             autocomplete: "new-password"
48210         };
48211     },
48212
48213     /**
48214      * Protected method that will not generally be called directly. It
48215      * is called when the editor creates its toolbar. Override this method if you need to
48216      * add custom toolbar buttons.
48217      * @param {HtmlEditor} editor
48218      */
48219     createToolbar : function(editor){
48220         Roo.log("create toolbars");
48221         if (!editor.toolbars || !editor.toolbars.length) {
48222             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48223         }
48224         
48225         for (var i =0 ; i < editor.toolbars.length;i++) {
48226             editor.toolbars[i] = Roo.factory(
48227                     typeof(editor.toolbars[i]) == 'string' ?
48228                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48229                 Roo.form.HtmlEditor);
48230             editor.toolbars[i].init(editor);
48231         }
48232          
48233         
48234     },
48235
48236      
48237     // private
48238     onRender : function(ct, position)
48239     {
48240         var _t = this;
48241         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48242         
48243         this.wrap = this.el.wrap({
48244             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48245         });
48246         
48247         this.editorcore.onRender(ct, position);
48248          
48249         if (this.resizable) {
48250             this.resizeEl = new Roo.Resizable(this.wrap, {
48251                 pinned : true,
48252                 wrap: true,
48253                 dynamic : true,
48254                 minHeight : this.height,
48255                 height: this.height,
48256                 handles : this.resizable,
48257                 width: this.width,
48258                 listeners : {
48259                     resize : function(r, w, h) {
48260                         _t.onResize(w,h); // -something
48261                     }
48262                 }
48263             });
48264             
48265         }
48266         this.createToolbar(this);
48267        
48268         
48269         if(!this.width){
48270             this.setSize(this.wrap.getSize());
48271         }
48272         if (this.resizeEl) {
48273             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48274             // should trigger onReize..
48275         }
48276         
48277         this.keyNav = new Roo.KeyNav(this.el, {
48278             
48279             "tab" : function(e){
48280                 e.preventDefault();
48281                 
48282                 var value = this.getValue();
48283                 
48284                 var start = this.el.dom.selectionStart;
48285                 var end = this.el.dom.selectionEnd;
48286                 
48287                 if(!e.shiftKey){
48288                     
48289                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48290                     this.el.dom.setSelectionRange(end + 1, end + 1);
48291                     return;
48292                 }
48293                 
48294                 var f = value.substring(0, start).split("\t");
48295                 
48296                 if(f.pop().length != 0){
48297                     return;
48298                 }
48299                 
48300                 this.setValue(f.join("\t") + value.substring(end));
48301                 this.el.dom.setSelectionRange(start - 1, start - 1);
48302                 
48303             },
48304             
48305             "home" : function(e){
48306                 e.preventDefault();
48307                 
48308                 var curr = this.el.dom.selectionStart;
48309                 var lines = this.getValue().split("\n");
48310                 
48311                 if(!lines.length){
48312                     return;
48313                 }
48314                 
48315                 if(e.ctrlKey){
48316                     this.el.dom.setSelectionRange(0, 0);
48317                     return;
48318                 }
48319                 
48320                 var pos = 0;
48321                 
48322                 for (var i = 0; i < lines.length;i++) {
48323                     pos += lines[i].length;
48324                     
48325                     if(i != 0){
48326                         pos += 1;
48327                     }
48328                     
48329                     if(pos < curr){
48330                         continue;
48331                     }
48332                     
48333                     pos -= lines[i].length;
48334                     
48335                     break;
48336                 }
48337                 
48338                 if(!e.shiftKey){
48339                     this.el.dom.setSelectionRange(pos, pos);
48340                     return;
48341                 }
48342                 
48343                 this.el.dom.selectionStart = pos;
48344                 this.el.dom.selectionEnd = curr;
48345             },
48346             
48347             "end" : function(e){
48348                 e.preventDefault();
48349                 
48350                 var curr = this.el.dom.selectionStart;
48351                 var lines = this.getValue().split("\n");
48352                 
48353                 if(!lines.length){
48354                     return;
48355                 }
48356                 
48357                 if(e.ctrlKey){
48358                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48359                     return;
48360                 }
48361                 
48362                 var pos = 0;
48363                 
48364                 for (var i = 0; i < lines.length;i++) {
48365                     
48366                     pos += lines[i].length;
48367                     
48368                     if(i != 0){
48369                         pos += 1;
48370                     }
48371                     
48372                     if(pos < curr){
48373                         continue;
48374                     }
48375                     
48376                     break;
48377                 }
48378                 
48379                 if(!e.shiftKey){
48380                     this.el.dom.setSelectionRange(pos, pos);
48381                     return;
48382                 }
48383                 
48384                 this.el.dom.selectionStart = curr;
48385                 this.el.dom.selectionEnd = pos;
48386             },
48387
48388             scope : this,
48389
48390             doRelay : function(foo, bar, hname){
48391                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48392             },
48393
48394             forceKeyDown: true
48395         });
48396         
48397 //        if(this.autosave && this.w){
48398 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48399 //        }
48400     },
48401
48402     // private
48403     onResize : function(w, h)
48404     {
48405         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48406         var ew = false;
48407         var eh = false;
48408         
48409         if(this.el ){
48410             if(typeof w == 'number'){
48411                 var aw = w - this.wrap.getFrameWidth('lr');
48412                 this.el.setWidth(this.adjustWidth('textarea', aw));
48413                 ew = aw;
48414             }
48415             if(typeof h == 'number'){
48416                 var tbh = 0;
48417                 for (var i =0; i < this.toolbars.length;i++) {
48418                     // fixme - ask toolbars for heights?
48419                     tbh += this.toolbars[i].tb.el.getHeight();
48420                     if (this.toolbars[i].footer) {
48421                         tbh += this.toolbars[i].footer.el.getHeight();
48422                     }
48423                 }
48424                 
48425                 
48426                 
48427                 
48428                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48429                 ah -= 5; // knock a few pixes off for look..
48430 //                Roo.log(ah);
48431                 this.el.setHeight(this.adjustWidth('textarea', ah));
48432                 var eh = ah;
48433             }
48434         }
48435         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48436         this.editorcore.onResize(ew,eh);
48437         
48438     },
48439
48440     /**
48441      * Toggles the editor between standard and source edit mode.
48442      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48443      */
48444     toggleSourceEdit : function(sourceEditMode)
48445     {
48446         this.editorcore.toggleSourceEdit(sourceEditMode);
48447         
48448         if(this.editorcore.sourceEditMode){
48449             Roo.log('editor - showing textarea');
48450             
48451 //            Roo.log('in');
48452 //            Roo.log(this.syncValue());
48453             this.editorcore.syncValue();
48454             this.el.removeClass('x-hidden');
48455             this.el.dom.removeAttribute('tabIndex');
48456             this.el.focus();
48457             this.el.dom.scrollTop = 0;
48458             
48459             
48460             for (var i = 0; i < this.toolbars.length; i++) {
48461                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48462                     this.toolbars[i].tb.hide();
48463                     this.toolbars[i].footer.hide();
48464                 }
48465             }
48466             
48467         }else{
48468             Roo.log('editor - hiding textarea');
48469 //            Roo.log('out')
48470 //            Roo.log(this.pushValue()); 
48471             this.editorcore.pushValue();
48472             
48473             this.el.addClass('x-hidden');
48474             this.el.dom.setAttribute('tabIndex', -1);
48475             
48476             for (var i = 0; i < this.toolbars.length; i++) {
48477                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48478                     this.toolbars[i].tb.show();
48479                     this.toolbars[i].footer.show();
48480                 }
48481             }
48482             
48483             //this.deferFocus();
48484         }
48485         
48486         this.setSize(this.wrap.getSize());
48487         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48488         
48489         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48490     },
48491  
48492     // private (for BoxComponent)
48493     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48494
48495     // private (for BoxComponent)
48496     getResizeEl : function(){
48497         return this.wrap;
48498     },
48499
48500     // private (for BoxComponent)
48501     getPositionEl : function(){
48502         return this.wrap;
48503     },
48504
48505     // private
48506     initEvents : function(){
48507         this.originalValue = this.getValue();
48508     },
48509
48510     /**
48511      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48512      * @method
48513      */
48514     markInvalid : Roo.emptyFn,
48515     /**
48516      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48517      * @method
48518      */
48519     clearInvalid : Roo.emptyFn,
48520
48521     setValue : function(v){
48522         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48523         this.editorcore.pushValue();
48524     },
48525
48526      
48527     // private
48528     deferFocus : function(){
48529         this.focus.defer(10, this);
48530     },
48531
48532     // doc'ed in Field
48533     focus : function(){
48534         this.editorcore.focus();
48535         
48536     },
48537       
48538
48539     // private
48540     onDestroy : function(){
48541         
48542         
48543         
48544         if(this.rendered){
48545             
48546             for (var i =0; i < this.toolbars.length;i++) {
48547                 // fixme - ask toolbars for heights?
48548                 this.toolbars[i].onDestroy();
48549             }
48550             
48551             this.wrap.dom.innerHTML = '';
48552             this.wrap.remove();
48553         }
48554     },
48555
48556     // private
48557     onFirstFocus : function(){
48558         //Roo.log("onFirstFocus");
48559         this.editorcore.onFirstFocus();
48560          for (var i =0; i < this.toolbars.length;i++) {
48561             this.toolbars[i].onFirstFocus();
48562         }
48563         
48564     },
48565     
48566     // private
48567     syncValue : function()
48568     {
48569         this.editorcore.syncValue();
48570     },
48571     
48572     pushValue : function()
48573     {
48574         this.editorcore.pushValue();
48575     },
48576     
48577     setStylesheets : function(stylesheets)
48578     {
48579         this.editorcore.setStylesheets(stylesheets);
48580     },
48581     
48582     removeStylesheets : function()
48583     {
48584         this.editorcore.removeStylesheets();
48585     }
48586      
48587     
48588     // hide stuff that is not compatible
48589     /**
48590      * @event blur
48591      * @hide
48592      */
48593     /**
48594      * @event change
48595      * @hide
48596      */
48597     /**
48598      * @event focus
48599      * @hide
48600      */
48601     /**
48602      * @event specialkey
48603      * @hide
48604      */
48605     /**
48606      * @cfg {String} fieldClass @hide
48607      */
48608     /**
48609      * @cfg {String} focusClass @hide
48610      */
48611     /**
48612      * @cfg {String} autoCreate @hide
48613      */
48614     /**
48615      * @cfg {String} inputType @hide
48616      */
48617     /**
48618      * @cfg {String} invalidClass @hide
48619      */
48620     /**
48621      * @cfg {String} invalidText @hide
48622      */
48623     /**
48624      * @cfg {String} msgFx @hide
48625      */
48626     /**
48627      * @cfg {String} validateOnBlur @hide
48628      */
48629 });
48630  
48631     // <script type="text/javascript">
48632 /*
48633  * Based on
48634  * Ext JS Library 1.1.1
48635  * Copyright(c) 2006-2007, Ext JS, LLC.
48636  *  
48637  
48638  */
48639
48640 /**
48641  * @class Roo.form.HtmlEditorToolbar1
48642  * Basic Toolbar
48643  * 
48644  * Usage:
48645  *
48646  new Roo.form.HtmlEditor({
48647     ....
48648     toolbars : [
48649         new Roo.form.HtmlEditorToolbar1({
48650             disable : { fonts: 1 , format: 1, ..., ... , ...],
48651             btns : [ .... ]
48652         })
48653     }
48654      
48655  * 
48656  * @cfg {Object} disable List of elements to disable..
48657  * @cfg {Array} btns List of additional buttons.
48658  * 
48659  * 
48660  * NEEDS Extra CSS? 
48661  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48662  */
48663  
48664 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48665 {
48666     
48667     Roo.apply(this, config);
48668     
48669     // default disabled, based on 'good practice'..
48670     this.disable = this.disable || {};
48671     Roo.applyIf(this.disable, {
48672         fontSize : true,
48673         colors : true,
48674         specialElements : true
48675     });
48676     
48677     
48678     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48679     // dont call parent... till later.
48680 }
48681
48682 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48683     
48684     tb: false,
48685     
48686     rendered: false,
48687     
48688     editor : false,
48689     editorcore : false,
48690     /**
48691      * @cfg {Object} disable  List of toolbar elements to disable
48692          
48693      */
48694     disable : false,
48695     
48696     
48697      /**
48698      * @cfg {String} createLinkText The default text for the create link prompt
48699      */
48700     createLinkText : 'Please enter the URL for the link:',
48701     /**
48702      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48703      */
48704     defaultLinkValue : 'http:/'+'/',
48705    
48706     
48707       /**
48708      * @cfg {Array} fontFamilies An array of available font families
48709      */
48710     fontFamilies : [
48711         'Arial',
48712         'Courier New',
48713         'Tahoma',
48714         'Times New Roman',
48715         'Verdana'
48716     ],
48717     
48718     specialChars : [
48719            "&#169;",
48720           "&#174;",     
48721           "&#8482;",    
48722           "&#163;" ,    
48723          // "&#8212;",    
48724           "&#8230;",    
48725           "&#247;" ,    
48726         //  "&#225;" ,     ?? a acute?
48727            "&#8364;"    , //Euro
48728        //   "&#8220;"    ,
48729         //  "&#8221;"    ,
48730         //  "&#8226;"    ,
48731           "&#176;"  //   , // degrees
48732
48733          // "&#233;"     , // e ecute
48734          // "&#250;"     , // u ecute?
48735     ],
48736     
48737     specialElements : [
48738         {
48739             text: "Insert Table",
48740             xtype: 'MenuItem',
48741             xns : Roo.Menu,
48742             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48743                 
48744         },
48745         {    
48746             text: "Insert Image",
48747             xtype: 'MenuItem',
48748             xns : Roo.Menu,
48749             ihtml : '<img src="about:blank"/>'
48750             
48751         }
48752         
48753          
48754     ],
48755     
48756     
48757     inputElements : [ 
48758             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48759             "input:submit", "input:button", "select", "textarea", "label" ],
48760     formats : [
48761         ["p"] ,  
48762         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48763         ["pre"],[ "code"], 
48764         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48765         ['div'],['span'],
48766         ['sup'],['sub']
48767     ],
48768     
48769     cleanStyles : [
48770         "font-size"
48771     ],
48772      /**
48773      * @cfg {String} defaultFont default font to use.
48774      */
48775     defaultFont: 'tahoma',
48776    
48777     fontSelect : false,
48778     
48779     
48780     formatCombo : false,
48781     
48782     init : function(editor)
48783     {
48784         this.editor = editor;
48785         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48786         var editorcore = this.editorcore;
48787         
48788         var _t = this;
48789         
48790         var fid = editorcore.frameId;
48791         var etb = this;
48792         function btn(id, toggle, handler){
48793             var xid = fid + '-'+ id ;
48794             return {
48795                 id : xid,
48796                 cmd : id,
48797                 cls : 'x-btn-icon x-edit-'+id,
48798                 enableToggle:toggle !== false,
48799                 scope: _t, // was editor...
48800                 handler:handler||_t.relayBtnCmd,
48801                 clickEvent:'mousedown',
48802                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48803                 tabIndex:-1
48804             };
48805         }
48806         
48807         
48808         
48809         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48810         this.tb = tb;
48811          // stop form submits
48812         tb.el.on('click', function(e){
48813             e.preventDefault(); // what does this do?
48814         });
48815
48816         if(!this.disable.font) { // && !Roo.isSafari){
48817             /* why no safari for fonts 
48818             editor.fontSelect = tb.el.createChild({
48819                 tag:'select',
48820                 tabIndex: -1,
48821                 cls:'x-font-select',
48822                 html: this.createFontOptions()
48823             });
48824             
48825             editor.fontSelect.on('change', function(){
48826                 var font = editor.fontSelect.dom.value;
48827                 editor.relayCmd('fontname', font);
48828                 editor.deferFocus();
48829             }, editor);
48830             
48831             tb.add(
48832                 editor.fontSelect.dom,
48833                 '-'
48834             );
48835             */
48836             
48837         };
48838         if(!this.disable.formats){
48839             this.formatCombo = new Roo.form.ComboBox({
48840                 store: new Roo.data.SimpleStore({
48841                     id : 'tag',
48842                     fields: ['tag'],
48843                     data : this.formats // from states.js
48844                 }),
48845                 blockFocus : true,
48846                 name : '',
48847                 //autoCreate : {tag: "div",  size: "20"},
48848                 displayField:'tag',
48849                 typeAhead: false,
48850                 mode: 'local',
48851                 editable : false,
48852                 triggerAction: 'all',
48853                 emptyText:'Add tag',
48854                 selectOnFocus:true,
48855                 width:135,
48856                 listeners : {
48857                     'select': function(c, r, i) {
48858                         editorcore.insertTag(r.get('tag'));
48859                         editor.focus();
48860                     }
48861                 }
48862
48863             });
48864             tb.addField(this.formatCombo);
48865             
48866         }
48867         
48868         if(!this.disable.format){
48869             tb.add(
48870                 btn('bold'),
48871                 btn('italic'),
48872                 btn('underline'),
48873                 btn('strikethrough')
48874             );
48875         };
48876         if(!this.disable.fontSize){
48877             tb.add(
48878                 '-',
48879                 
48880                 
48881                 btn('increasefontsize', false, editorcore.adjustFont),
48882                 btn('decreasefontsize', false, editorcore.adjustFont)
48883             );
48884         };
48885         
48886         
48887         if(!this.disable.colors){
48888             tb.add(
48889                 '-', {
48890                     id:editorcore.frameId +'-forecolor',
48891                     cls:'x-btn-icon x-edit-forecolor',
48892                     clickEvent:'mousedown',
48893                     tooltip: this.buttonTips['forecolor'] || undefined,
48894                     tabIndex:-1,
48895                     menu : new Roo.menu.ColorMenu({
48896                         allowReselect: true,
48897                         focus: Roo.emptyFn,
48898                         value:'000000',
48899                         plain:true,
48900                         selectHandler: function(cp, color){
48901                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48902                             editor.deferFocus();
48903                         },
48904                         scope: editorcore,
48905                         clickEvent:'mousedown'
48906                     })
48907                 }, {
48908                     id:editorcore.frameId +'backcolor',
48909                     cls:'x-btn-icon x-edit-backcolor',
48910                     clickEvent:'mousedown',
48911                     tooltip: this.buttonTips['backcolor'] || undefined,
48912                     tabIndex:-1,
48913                     menu : new Roo.menu.ColorMenu({
48914                         focus: Roo.emptyFn,
48915                         value:'FFFFFF',
48916                         plain:true,
48917                         allowReselect: true,
48918                         selectHandler: function(cp, color){
48919                             if(Roo.isGecko){
48920                                 editorcore.execCmd('useCSS', false);
48921                                 editorcore.execCmd('hilitecolor', color);
48922                                 editorcore.execCmd('useCSS', true);
48923                                 editor.deferFocus();
48924                             }else{
48925                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48926                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48927                                 editor.deferFocus();
48928                             }
48929                         },
48930                         scope:editorcore,
48931                         clickEvent:'mousedown'
48932                     })
48933                 }
48934             );
48935         };
48936         // now add all the items...
48937         
48938
48939         if(!this.disable.alignments){
48940             tb.add(
48941                 '-',
48942                 btn('justifyleft'),
48943                 btn('justifycenter'),
48944                 btn('justifyright')
48945             );
48946         };
48947
48948         //if(!Roo.isSafari){
48949             if(!this.disable.links){
48950                 tb.add(
48951                     '-',
48952                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48953                 );
48954             };
48955
48956             if(!this.disable.lists){
48957                 tb.add(
48958                     '-',
48959                     btn('insertorderedlist'),
48960                     btn('insertunorderedlist')
48961                 );
48962             }
48963             if(!this.disable.sourceEdit){
48964                 tb.add(
48965                     '-',
48966                     btn('sourceedit', true, function(btn){
48967                         this.toggleSourceEdit(btn.pressed);
48968                     })
48969                 );
48970             }
48971         //}
48972         
48973         var smenu = { };
48974         // special menu.. - needs to be tidied up..
48975         if (!this.disable.special) {
48976             smenu = {
48977                 text: "&#169;",
48978                 cls: 'x-edit-none',
48979                 
48980                 menu : {
48981                     items : []
48982                 }
48983             };
48984             for (var i =0; i < this.specialChars.length; i++) {
48985                 smenu.menu.items.push({
48986                     
48987                     html: this.specialChars[i],
48988                     handler: function(a,b) {
48989                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48990                         //editor.insertAtCursor(a.html);
48991                         
48992                     },
48993                     tabIndex:-1
48994                 });
48995             }
48996             
48997             
48998             tb.add(smenu);
48999             
49000             
49001         }
49002         
49003         var cmenu = { };
49004         if (!this.disable.cleanStyles) {
49005             cmenu = {
49006                 cls: 'x-btn-icon x-btn-clear',
49007                 
49008                 menu : {
49009                     items : []
49010                 }
49011             };
49012             for (var i =0; i < this.cleanStyles.length; i++) {
49013                 cmenu.menu.items.push({
49014                     actiontype : this.cleanStyles[i],
49015                     html: 'Remove ' + this.cleanStyles[i],
49016                     handler: function(a,b) {
49017 //                        Roo.log(a);
49018 //                        Roo.log(b);
49019                         var c = Roo.get(editorcore.doc.body);
49020                         c.select('[style]').each(function(s) {
49021                             s.dom.style.removeProperty(a.actiontype);
49022                         });
49023                         editorcore.syncValue();
49024                     },
49025                     tabIndex:-1
49026                 });
49027             }
49028             cmenu.menu.items.push({
49029                 actiontype : 'tablewidths',
49030                 html: 'Remove Table Widths',
49031                 handler: function(a,b) {
49032                     editorcore.cleanTableWidths();
49033                     editorcore.syncValue();
49034                 },
49035                 tabIndex:-1
49036             });
49037             cmenu.menu.items.push({
49038                 actiontype : 'word',
49039                 html: 'Remove MS Word Formating',
49040                 handler: function(a,b) {
49041                     editorcore.cleanWord();
49042                     editorcore.syncValue();
49043                 },
49044                 tabIndex:-1
49045             });
49046             
49047             cmenu.menu.items.push({
49048                 actiontype : 'all',
49049                 html: 'Remove All Styles',
49050                 handler: function(a,b) {
49051                     
49052                     var c = Roo.get(editorcore.doc.body);
49053                     c.select('[style]').each(function(s) {
49054                         s.dom.removeAttribute('style');
49055                     });
49056                     editorcore.syncValue();
49057                 },
49058                 tabIndex:-1
49059             });
49060             
49061             cmenu.menu.items.push({
49062                 actiontype : 'all',
49063                 html: 'Remove All CSS Classes',
49064                 handler: function(a,b) {
49065                     
49066                     var c = Roo.get(editorcore.doc.body);
49067                     c.select('[class]').each(function(s) {
49068                         s.dom.removeAttribute('class');
49069                     });
49070                     editorcore.cleanWord();
49071                     editorcore.syncValue();
49072                 },
49073                 tabIndex:-1
49074             });
49075             
49076              cmenu.menu.items.push({
49077                 actiontype : 'tidy',
49078                 html: 'Tidy HTML Source',
49079                 handler: function(a,b) {
49080                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49081                     editorcore.syncValue();
49082                 },
49083                 tabIndex:-1
49084             });
49085             
49086             
49087             tb.add(cmenu);
49088         }
49089          
49090         if (!this.disable.specialElements) {
49091             var semenu = {
49092                 text: "Other;",
49093                 cls: 'x-edit-none',
49094                 menu : {
49095                     items : []
49096                 }
49097             };
49098             for (var i =0; i < this.specialElements.length; i++) {
49099                 semenu.menu.items.push(
49100                     Roo.apply({ 
49101                         handler: function(a,b) {
49102                             editor.insertAtCursor(this.ihtml);
49103                         }
49104                     }, this.specialElements[i])
49105                 );
49106                     
49107             }
49108             
49109             tb.add(semenu);
49110             
49111             
49112         }
49113          
49114         
49115         if (this.btns) {
49116             for(var i =0; i< this.btns.length;i++) {
49117                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49118                 b.cls =  'x-edit-none';
49119                 
49120                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49121                     b.cls += ' x-init-enable';
49122                 }
49123                 
49124                 b.scope = editorcore;
49125                 tb.add(b);
49126             }
49127         
49128         }
49129         
49130         
49131         
49132         // disable everything...
49133         
49134         this.tb.items.each(function(item){
49135             
49136            if(
49137                 item.id != editorcore.frameId+ '-sourceedit' && 
49138                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49139             ){
49140                 
49141                 item.disable();
49142             }
49143         });
49144         this.rendered = true;
49145         
49146         // the all the btns;
49147         editor.on('editorevent', this.updateToolbar, this);
49148         // other toolbars need to implement this..
49149         //editor.on('editmodechange', this.updateToolbar, this);
49150     },
49151     
49152     
49153     relayBtnCmd : function(btn) {
49154         this.editorcore.relayCmd(btn.cmd);
49155     },
49156     // private used internally
49157     createLink : function(){
49158         Roo.log("create link?");
49159         var url = prompt(this.createLinkText, this.defaultLinkValue);
49160         if(url && url != 'http:/'+'/'){
49161             this.editorcore.relayCmd('createlink', url);
49162         }
49163     },
49164
49165     
49166     /**
49167      * Protected method that will not generally be called directly. It triggers
49168      * a toolbar update by reading the markup state of the current selection in the editor.
49169      */
49170     updateToolbar: function(){
49171
49172         if(!this.editorcore.activated){
49173             this.editor.onFirstFocus();
49174             return;
49175         }
49176
49177         var btns = this.tb.items.map, 
49178             doc = this.editorcore.doc,
49179             frameId = this.editorcore.frameId;
49180
49181         if(!this.disable.font && !Roo.isSafari){
49182             /*
49183             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49184             if(name != this.fontSelect.dom.value){
49185                 this.fontSelect.dom.value = name;
49186             }
49187             */
49188         }
49189         if(!this.disable.format){
49190             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49191             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49192             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49193             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49194         }
49195         if(!this.disable.alignments){
49196             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49197             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49198             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49199         }
49200         if(!Roo.isSafari && !this.disable.lists){
49201             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49202             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49203         }
49204         
49205         var ans = this.editorcore.getAllAncestors();
49206         if (this.formatCombo) {
49207             
49208             
49209             var store = this.formatCombo.store;
49210             this.formatCombo.setValue("");
49211             for (var i =0; i < ans.length;i++) {
49212                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49213                     // select it..
49214                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49215                     break;
49216                 }
49217             }
49218         }
49219         
49220         
49221         
49222         // hides menus... - so this cant be on a menu...
49223         Roo.menu.MenuMgr.hideAll();
49224
49225         //this.editorsyncValue();
49226     },
49227    
49228     
49229     createFontOptions : function(){
49230         var buf = [], fs = this.fontFamilies, ff, lc;
49231         
49232         
49233         
49234         for(var i = 0, len = fs.length; i< len; i++){
49235             ff = fs[i];
49236             lc = ff.toLowerCase();
49237             buf.push(
49238                 '<option value="',lc,'" style="font-family:',ff,';"',
49239                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49240                     ff,
49241                 '</option>'
49242             );
49243         }
49244         return buf.join('');
49245     },
49246     
49247     toggleSourceEdit : function(sourceEditMode){
49248         
49249         Roo.log("toolbar toogle");
49250         if(sourceEditMode === undefined){
49251             sourceEditMode = !this.sourceEditMode;
49252         }
49253         this.sourceEditMode = sourceEditMode === true;
49254         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49255         // just toggle the button?
49256         if(btn.pressed !== this.sourceEditMode){
49257             btn.toggle(this.sourceEditMode);
49258             return;
49259         }
49260         
49261         if(sourceEditMode){
49262             Roo.log("disabling buttons");
49263             this.tb.items.each(function(item){
49264                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49265                     item.disable();
49266                 }
49267             });
49268           
49269         }else{
49270             Roo.log("enabling buttons");
49271             if(this.editorcore.initialized){
49272                 this.tb.items.each(function(item){
49273                     item.enable();
49274                 });
49275             }
49276             
49277         }
49278         Roo.log("calling toggole on editor");
49279         // tell the editor that it's been pressed..
49280         this.editor.toggleSourceEdit(sourceEditMode);
49281        
49282     },
49283      /**
49284      * Object collection of toolbar tooltips for the buttons in the editor. The key
49285      * is the command id associated with that button and the value is a valid QuickTips object.
49286      * For example:
49287 <pre><code>
49288 {
49289     bold : {
49290         title: 'Bold (Ctrl+B)',
49291         text: 'Make the selected text bold.',
49292         cls: 'x-html-editor-tip'
49293     },
49294     italic : {
49295         title: 'Italic (Ctrl+I)',
49296         text: 'Make the selected text italic.',
49297         cls: 'x-html-editor-tip'
49298     },
49299     ...
49300 </code></pre>
49301     * @type Object
49302      */
49303     buttonTips : {
49304         bold : {
49305             title: 'Bold (Ctrl+B)',
49306             text: 'Make the selected text bold.',
49307             cls: 'x-html-editor-tip'
49308         },
49309         italic : {
49310             title: 'Italic (Ctrl+I)',
49311             text: 'Make the selected text italic.',
49312             cls: 'x-html-editor-tip'
49313         },
49314         underline : {
49315             title: 'Underline (Ctrl+U)',
49316             text: 'Underline the selected text.',
49317             cls: 'x-html-editor-tip'
49318         },
49319         strikethrough : {
49320             title: 'Strikethrough',
49321             text: 'Strikethrough the selected text.',
49322             cls: 'x-html-editor-tip'
49323         },
49324         increasefontsize : {
49325             title: 'Grow Text',
49326             text: 'Increase the font size.',
49327             cls: 'x-html-editor-tip'
49328         },
49329         decreasefontsize : {
49330             title: 'Shrink Text',
49331             text: 'Decrease the font size.',
49332             cls: 'x-html-editor-tip'
49333         },
49334         backcolor : {
49335             title: 'Text Highlight Color',
49336             text: 'Change the background color of the selected text.',
49337             cls: 'x-html-editor-tip'
49338         },
49339         forecolor : {
49340             title: 'Font Color',
49341             text: 'Change the color of the selected text.',
49342             cls: 'x-html-editor-tip'
49343         },
49344         justifyleft : {
49345             title: 'Align Text Left',
49346             text: 'Align text to the left.',
49347             cls: 'x-html-editor-tip'
49348         },
49349         justifycenter : {
49350             title: 'Center Text',
49351             text: 'Center text in the editor.',
49352             cls: 'x-html-editor-tip'
49353         },
49354         justifyright : {
49355             title: 'Align Text Right',
49356             text: 'Align text to the right.',
49357             cls: 'x-html-editor-tip'
49358         },
49359         insertunorderedlist : {
49360             title: 'Bullet List',
49361             text: 'Start a bulleted list.',
49362             cls: 'x-html-editor-tip'
49363         },
49364         insertorderedlist : {
49365             title: 'Numbered List',
49366             text: 'Start a numbered list.',
49367             cls: 'x-html-editor-tip'
49368         },
49369         createlink : {
49370             title: 'Hyperlink',
49371             text: 'Make the selected text a hyperlink.',
49372             cls: 'x-html-editor-tip'
49373         },
49374         sourceedit : {
49375             title: 'Source Edit',
49376             text: 'Switch to source editing mode.',
49377             cls: 'x-html-editor-tip'
49378         }
49379     },
49380     // private
49381     onDestroy : function(){
49382         if(this.rendered){
49383             
49384             this.tb.items.each(function(item){
49385                 if(item.menu){
49386                     item.menu.removeAll();
49387                     if(item.menu.el){
49388                         item.menu.el.destroy();
49389                     }
49390                 }
49391                 item.destroy();
49392             });
49393              
49394         }
49395     },
49396     onFirstFocus: function() {
49397         this.tb.items.each(function(item){
49398            item.enable();
49399         });
49400     }
49401 });
49402
49403
49404
49405
49406 // <script type="text/javascript">
49407 /*
49408  * Based on
49409  * Ext JS Library 1.1.1
49410  * Copyright(c) 2006-2007, Ext JS, LLC.
49411  *  
49412  
49413  */
49414
49415  
49416 /**
49417  * @class Roo.form.HtmlEditor.ToolbarContext
49418  * Context Toolbar
49419  * 
49420  * Usage:
49421  *
49422  new Roo.form.HtmlEditor({
49423     ....
49424     toolbars : [
49425         { xtype: 'ToolbarStandard', styles : {} }
49426         { xtype: 'ToolbarContext', disable : {} }
49427     ]
49428 })
49429
49430      
49431  * 
49432  * @config : {Object} disable List of elements to disable.. (not done yet.)
49433  * @config : {Object} styles  Map of styles available.
49434  * 
49435  */
49436
49437 Roo.form.HtmlEditor.ToolbarContext = function(config)
49438 {
49439     
49440     Roo.apply(this, config);
49441     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49442     // dont call parent... till later.
49443     this.styles = this.styles || {};
49444 }
49445
49446  
49447
49448 Roo.form.HtmlEditor.ToolbarContext.types = {
49449     'IMG' : [
49450         {
49451             name : 'width',
49452             title: "Width",
49453             width: 40
49454         },
49455         {
49456             name : 'height',
49457             title: "Height",
49458             width: 40
49459         },
49460         {
49461             name : 'align',
49462             title: "Align",
49463             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49464             width : 80
49465             
49466         },
49467         {
49468             name : 'border',
49469             title: "Border",
49470             width: 40
49471         },
49472         {
49473             name : 'alt',
49474             title: "Alt",
49475             width: 120
49476         },
49477         {
49478             name : 'src',
49479             title: "Src",
49480             width: 220
49481         }
49482         
49483     ],
49484     
49485     'FIGURE' : [
49486         {
49487             name : 'align',
49488             title: "Align",
49489             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49490             width : 80  
49491         }
49492     ],
49493     'A' : [
49494         {
49495             name : 'name',
49496             title: "Name",
49497             width: 50
49498         },
49499         {
49500             name : 'target',
49501             title: "Target",
49502             width: 120
49503         },
49504         {
49505             name : 'href',
49506             title: "Href",
49507             width: 220
49508         } // border?
49509         
49510     ],
49511     
49512     'INPUT' : [
49513         {
49514             name : 'name',
49515             title: "name",
49516             width: 120
49517         },
49518         {
49519             name : 'value',
49520             title: "Value",
49521             width: 120
49522         },
49523         {
49524             name : 'width',
49525             title: "Width",
49526             width: 40
49527         }
49528     ],
49529     'LABEL' : [
49530          {
49531             name : 'for',
49532             title: "For",
49533             width: 120
49534         }
49535     ],
49536     'TEXTAREA' : [
49537         {
49538             name : 'name',
49539             title: "name",
49540             width: 120
49541         },
49542         {
49543             name : 'rows',
49544             title: "Rows",
49545             width: 20
49546         },
49547         {
49548             name : 'cols',
49549             title: "Cols",
49550             width: 20
49551         }
49552     ],
49553     'SELECT' : [
49554         {
49555             name : 'name',
49556             title: "name",
49557             width: 120
49558         },
49559         {
49560             name : 'selectoptions',
49561             title: "Options",
49562             width: 200
49563         }
49564     ],
49565     
49566     // should we really allow this??
49567     // should this just be 
49568     'BODY' : [
49569         
49570         {
49571             name : 'title',
49572             title: "Title",
49573             width: 200,
49574             disabled : true
49575         }
49576     ],
49577  
49578     '*' : [
49579         // empty.
49580     ]
49581
49582 };
49583
49584 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49585 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49586
49587 Roo.form.HtmlEditor.ToolbarContext.options = {
49588         'font-family'  : [ 
49589                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49590                 [ 'Courier New', 'Courier New'],
49591                 [ 'Tahoma', 'Tahoma'],
49592                 [ 'Times New Roman,serif', 'Times'],
49593                 [ 'Verdana','Verdana' ]
49594         ]
49595 };
49596
49597 // fixme - these need to be configurable..
49598  
49599
49600 //Roo.form.HtmlEditor.ToolbarContext.types
49601
49602
49603 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49604     
49605     tb: false,
49606     
49607     rendered: false,
49608     
49609     editor : false,
49610     editorcore : false,
49611     /**
49612      * @cfg {Object} disable  List of toolbar elements to disable
49613          
49614      */
49615     disable : false,
49616     /**
49617      * @cfg {Object} styles List of styles 
49618      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49619      *
49620      * These must be defined in the page, so they get rendered correctly..
49621      * .headline { }
49622      * TD.underline { }
49623      * 
49624      */
49625     styles : false,
49626     
49627     options: false,
49628     
49629     toolbars : false,
49630     
49631     init : function(editor)
49632     {
49633         this.editor = editor;
49634         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49635         var editorcore = this.editorcore;
49636         
49637         var fid = editorcore.frameId;
49638         var etb = this;
49639         function btn(id, toggle, handler){
49640             var xid = fid + '-'+ id ;
49641             return {
49642                 id : xid,
49643                 cmd : id,
49644                 cls : 'x-btn-icon x-edit-'+id,
49645                 enableToggle:toggle !== false,
49646                 scope: editorcore, // was editor...
49647                 handler:handler||editorcore.relayBtnCmd,
49648                 clickEvent:'mousedown',
49649                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49650                 tabIndex:-1
49651             };
49652         }
49653         // create a new element.
49654         var wdiv = editor.wrap.createChild({
49655                 tag: 'div'
49656             }, editor.wrap.dom.firstChild.nextSibling, true);
49657         
49658         // can we do this more than once??
49659         
49660          // stop form submits
49661       
49662  
49663         // disable everything...
49664         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49665         this.toolbars = {};
49666            
49667         for (var i in  ty) {
49668           
49669             this.toolbars[i] = this.buildToolbar(ty[i],i);
49670         }
49671         this.tb = this.toolbars.BODY;
49672         this.tb.el.show();
49673         this.buildFooter();
49674         this.footer.show();
49675         editor.on('hide', function( ) { this.footer.hide() }, this);
49676         editor.on('show', function( ) { this.footer.show() }, this);
49677         
49678          
49679         this.rendered = true;
49680         
49681         // the all the btns;
49682         editor.on('editorevent', this.updateToolbar, this);
49683         // other toolbars need to implement this..
49684         //editor.on('editmodechange', this.updateToolbar, this);
49685     },
49686     
49687     
49688     
49689     /**
49690      * Protected method that will not generally be called directly. It triggers
49691      * a toolbar update by reading the markup state of the current selection in the editor.
49692      *
49693      * Note you can force an update by calling on('editorevent', scope, false)
49694      */
49695     updateToolbar: function(editor ,ev, sel)
49696     {
49697         
49698         if (ev) {
49699             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49700         }
49701         
49702         //Roo.log(ev);
49703         // capture mouse up - this is handy for selecting images..
49704         // perhaps should go somewhere else...
49705         if(!this.editorcore.activated){
49706              this.editor.onFirstFocus();
49707             return;
49708         }
49709         //Roo.log(ev ? ev.target : 'NOTARGET');
49710         
49711         
49712         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49713         // selectNode - might want to handle IE?
49714         
49715         
49716         
49717         if (ev &&
49718             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49719             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49720             // they have click on an image...
49721             // let's see if we can change the selection...
49722             sel = ev.target;
49723             
49724             // this triggers looping?
49725             //this.editorcore.selectNode(sel);
49726              
49727         }  
49728         
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         var sel_el = Roo.get(sel);
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             db = sel_el.findParent('[data-block]');
49758             var cepar = sel_el.findParent('[contenteditable=true]');
49759             if (db && cepar && cepar.tagName != 'BODY') {
49760                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49761             }   
49762         }
49763         
49764         
49765         var block = false;
49766         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49767         if (db) {
49768             block = Roo.htmleditor.Block.factory(db);
49769             if (block) {
49770                 tn = 'BLOCK.' + db.getAttribute('data-block');
49771                 
49772                 //this.editorcore.selectNode(db);
49773                 if (typeof(this.toolbars[tn]) == 'undefined') {
49774                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49775                 }
49776                 this.toolbars[tn].selectedNode = db;
49777                 left_label = block.friendly_name;
49778                 ans = this.editorcore.getAllAncestors();
49779             }
49780             
49781                 
49782             
49783         }
49784         
49785         
49786         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49787             return; // no change?
49788         }
49789         
49790         
49791           
49792         this.tb.el.hide();
49793         ///console.log("show: " + tn);
49794         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49795         
49796         this.tb.el.show();
49797         // update name
49798         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49799         
49800         
49801         // update attributes
49802         if (block) {
49803              
49804             this.tb.fields.each(function(e) {
49805                 e.setValue(block[e.name]);
49806             });
49807             
49808             
49809         } else  if (this.tb.fields && this.tb.selectedNode) {
49810             this.tb.fields.each( function(e) {
49811                 if (e.stylename) {
49812                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49813                     return;
49814                 } 
49815                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49816             }, this);
49817             this.updateToolbarStyles(this.tb.selectedNode);  
49818         }
49819         
49820         
49821        
49822         Roo.menu.MenuMgr.hideAll();
49823
49824         
49825         
49826     
49827         // update the footer
49828         //
49829         this.updateFooter(ans);
49830              
49831     },
49832     
49833     updateToolbarStyles : function(sel)
49834     {
49835         var hasStyles = false;
49836         for(var i in this.styles) {
49837             hasStyles = true;
49838             break;
49839         }
49840         
49841         // update styles
49842         if (hasStyles && this.tb.hasStyles) { 
49843             var st = this.tb.fields.item(0);
49844             
49845             st.store.removeAll();
49846             var cn = sel.className.split(/\s+/);
49847             
49848             var avs = [];
49849             if (this.styles['*']) {
49850                 
49851                 Roo.each(this.styles['*'], function(v) {
49852                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49853                 });
49854             }
49855             if (this.styles[tn]) { 
49856                 Roo.each(this.styles[tn], function(v) {
49857                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49858                 });
49859             }
49860             
49861             st.store.loadData(avs);
49862             st.collapse();
49863             st.setValue(cn);
49864         }
49865     },
49866     
49867      
49868     updateFooter : function(ans)
49869     {
49870         var html = '';
49871         if (ans === false) {
49872             this.footDisp.dom.innerHTML = '';
49873             return;
49874         }
49875         
49876         this.footerEls = ans.reverse();
49877         Roo.each(this.footerEls, function(a,i) {
49878             if (!a) { return; }
49879             html += html.length ? ' &gt; '  :  '';
49880             
49881             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49882             
49883         });
49884        
49885         // 
49886         var sz = this.footDisp.up('td').getSize();
49887         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49888         this.footDisp.dom.style.marginLeft = '5px';
49889         
49890         this.footDisp.dom.style.overflow = 'hidden';
49891         
49892         this.footDisp.dom.innerHTML = html;
49893             
49894         
49895     },
49896    
49897        
49898     // private
49899     onDestroy : function(){
49900         if(this.rendered){
49901             
49902             this.tb.items.each(function(item){
49903                 if(item.menu){
49904                     item.menu.removeAll();
49905                     if(item.menu.el){
49906                         item.menu.el.destroy();
49907                     }
49908                 }
49909                 item.destroy();
49910             });
49911              
49912         }
49913     },
49914     onFirstFocus: function() {
49915         // need to do this for all the toolbars..
49916         this.tb.items.each(function(item){
49917            item.enable();
49918         });
49919     },
49920     buildToolbar: function(tlist, nm, friendly_name, block)
49921     {
49922         var editor = this.editor;
49923         var editorcore = this.editorcore;
49924          // create a new element.
49925         var wdiv = editor.wrap.createChild({
49926                 tag: 'div'
49927             }, editor.wrap.dom.firstChild.nextSibling, true);
49928         
49929        
49930         var tb = new Roo.Toolbar(wdiv);
49931         ///this.tb = tb; // << this sets the active toolbar..
49932         if (tlist === false && block) {
49933             tlist = block.contextMenu(this);
49934         }
49935         
49936         tb.hasStyles = false;
49937         tb.name = nm;
49938         
49939         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49940         
49941         var styles = Array.from(this.styles);
49942         
49943         
49944         // styles...
49945         if (styles && styles.length) {
49946             tb.hasStyles = true;
49947             // this needs a multi-select checkbox...
49948             tb.addField( new Roo.form.ComboBox({
49949                 store: new Roo.data.SimpleStore({
49950                     id : 'val',
49951                     fields: ['val', 'selected'],
49952                     data : [] 
49953                 }),
49954                 name : '-roo-edit-className',
49955                 attrname : 'className',
49956                 displayField: 'val',
49957                 typeAhead: false,
49958                 mode: 'local',
49959                 editable : false,
49960                 triggerAction: 'all',
49961                 emptyText:'Select Style',
49962                 selectOnFocus:true,
49963                 width: 130,
49964                 listeners : {
49965                     'select': function(c, r, i) {
49966                         // initial support only for on class per el..
49967                         tb.selectedNode.className =  r ? r.get('val') : '';
49968                         editorcore.syncValue();
49969                     }
49970                 }
49971     
49972             }));
49973         }
49974         
49975         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49976         
49977         
49978         for (var i = 0; i < tlist.length; i++) {
49979             
49980             // newer versions will use xtype cfg to create menus.
49981             if (typeof(tlist[i].xtype) != 'undefined') {
49982                 
49983                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49984                 
49985                 
49986                 continue;
49987             }
49988             
49989             var item = tlist[i];
49990             tb.add(item.title + ":&nbsp;");
49991             
49992             
49993             //optname == used so you can configure the options available..
49994             var opts = item.opts ? item.opts : false;
49995             if (item.optname) { // use the b
49996                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49997            
49998             }
49999             
50000             if (opts) {
50001                 // opts == pulldown..
50002                 tb.addField( new Roo.form.ComboBox({
50003                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50004                         id : 'val',
50005                         fields: ['val', 'display'],
50006                         data : opts  
50007                     }),
50008                     name : '-roo-edit-' + tlist[i].name,
50009                     
50010                     attrname : tlist[i].name,
50011                     stylename : item.style ? item.style : false,
50012                     
50013                     displayField: item.displayField ? item.displayField : 'val',
50014                     valueField :  'val',
50015                     typeAhead: false,
50016                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50017                     editable : false,
50018                     triggerAction: 'all',
50019                     emptyText:'Select',
50020                     selectOnFocus:true,
50021                     width: item.width ? item.width  : 130,
50022                     listeners : {
50023                         'select': function(c, r, i) {
50024                             if (tb.selectedNode.hasAttribute('data-block')) {
50025                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50026                                 b[c.attrname] = r.get('val');
50027                                 b.updateElement(tb.selectedNode);
50028                                 editorcore.syncValue();
50029                                 return;
50030                             }
50031                             
50032                             if (c.stylename) {
50033                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50034                                 editorcore.syncValue();
50035                                 return;
50036                             }
50037                             if (r === false) {
50038                                 tb.selectedNode.removeAttribute(c.attrname);
50039                                 editorcore.syncValue();
50040                                 return;
50041                             }
50042                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50043                             editorcore.syncValue();
50044                         }
50045                     }
50046
50047                 }));
50048                 continue;
50049                     
50050                  
50051                 /*
50052                 tb.addField( new Roo.form.TextField({
50053                     name: i,
50054                     width: 100,
50055                     //allowBlank:false,
50056                     value: ''
50057                 }));
50058                 continue;
50059                 */
50060             }
50061             tb.addField( new Roo.form.TextField({
50062                 name: '-roo-edit-' + tlist[i].name,
50063                 attrname : tlist[i].name,
50064                 
50065                 width: item.width,
50066                 //allowBlank:true,
50067                 value: '',
50068                 listeners: {
50069                     'change' : function(f, nv, ov) {
50070                         
50071                         if (tb.selectedNode.hasAttribute('data-block')) {
50072                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50073                             b[f.attrname] = nv;
50074                             b.updateElement(tb.selectedNode);
50075                             editorcore.syncValue();
50076                             return;
50077                         }
50078                         
50079                         tb.selectedNode.setAttribute(f.attrname, nv);
50080                         editorcore.syncValue();
50081                     }
50082                 }
50083             }));
50084              
50085         }
50086         
50087         var _this = this;
50088         
50089         if(nm == 'BODY'){
50090             tb.addSeparator();
50091         
50092             tb.addButton( {
50093                 text: 'Stylesheets',
50094
50095                 listeners : {
50096                     click : function ()
50097                     {
50098                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50099                     }
50100                 }
50101             });
50102         }
50103         
50104         tb.addFill();
50105         tb.addButton({
50106             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50107     
50108             listeners : {
50109                 click : function ()
50110                 {
50111                     var sn = tb.selectedNode;
50112                     if (block) {
50113                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50114                         
50115                     }
50116                     if (!sn) {
50117                         return;
50118                     }
50119                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50120                     if (sn.hasAttribute('data-block')) {
50121                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50122                         sn.parentNode.removeChild(sn);
50123                         
50124                     } else if (sn && sn.tagName != 'BODY') {
50125                         // remove and keep parents.
50126                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50127                         a.removeTag(sn);
50128                     }
50129                     
50130                     
50131                     var range = editorcore.createRange();
50132         
50133                     range.setStart(stn,0);
50134                     range.setEnd(stn,0); 
50135                     var selection = editorcore.getSelection();
50136                     selection.removeAllRanges();
50137                     selection.addRange(range);
50138                     
50139                     
50140                     //_this.updateToolbar(null, null, pn);
50141                     _this.updateToolbar(null, null, null);
50142                     _this.updateFooter(false);
50143                     
50144                 }
50145             }
50146             
50147                     
50148                 
50149             
50150         });
50151         
50152         
50153         tb.el.on('click', function(e){
50154             e.preventDefault(); // what does this do?
50155         });
50156         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50157         tb.el.hide();
50158         
50159         // dont need to disable them... as they will get hidden
50160         return tb;
50161          
50162         
50163     },
50164     buildFooter : function()
50165     {
50166         
50167         var fel = this.editor.wrap.createChild();
50168         this.footer = new Roo.Toolbar(fel);
50169         // toolbar has scrolly on left / right?
50170         var footDisp= new Roo.Toolbar.Fill();
50171         var _t = this;
50172         this.footer.add(
50173             {
50174                 text : '&lt;',
50175                 xtype: 'Button',
50176                 handler : function() {
50177                     _t.footDisp.scrollTo('left',0,true)
50178                 }
50179             }
50180         );
50181         this.footer.add( footDisp );
50182         this.footer.add( 
50183             {
50184                 text : '&gt;',
50185                 xtype: 'Button',
50186                 handler : function() {
50187                     // no animation..
50188                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50189                 }
50190             }
50191         );
50192         var fel = Roo.get(footDisp.el);
50193         fel.addClass('x-editor-context');
50194         this.footDispWrap = fel; 
50195         this.footDispWrap.overflow  = 'hidden';
50196         
50197         this.footDisp = fel.createChild();
50198         this.footDispWrap.on('click', this.onContextClick, this)
50199         
50200         
50201     },
50202     // when the footer contect changes
50203     onContextClick : function (ev,dom)
50204     {
50205         ev.preventDefault();
50206         var  cn = dom.className;
50207         //Roo.log(cn);
50208         if (!cn.match(/x-ed-loc-/)) {
50209             return;
50210         }
50211         var n = cn.split('-').pop();
50212         var ans = this.footerEls;
50213         var sel = ans[n];
50214         
50215         this.editorcore.selectNode(sel);
50216         
50217         
50218         this.updateToolbar(null, null, sel);
50219         
50220         
50221     }
50222     
50223     
50224     
50225     
50226     
50227 });
50228
50229
50230
50231
50232
50233 /*
50234  * Based on:
50235  * Ext JS Library 1.1.1
50236  * Copyright(c) 2006-2007, Ext JS, LLC.
50237  *
50238  * Originally Released Under LGPL - original licence link has changed is not relivant.
50239  *
50240  * Fork - LGPL
50241  * <script type="text/javascript">
50242  */
50243  
50244 /**
50245  * @class Roo.form.BasicForm
50246  * @extends Roo.util.Observable
50247  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50248  * @constructor
50249  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50250  * @param {Object} config Configuration options
50251  */
50252 Roo.form.BasicForm = function(el, config){
50253     this.allItems = [];
50254     this.childForms = [];
50255     Roo.apply(this, config);
50256     /*
50257      * The Roo.form.Field items in this form.
50258      * @type MixedCollection
50259      */
50260      
50261      
50262     this.items = new Roo.util.MixedCollection(false, function(o){
50263         return o.id || (o.id = Roo.id());
50264     });
50265     this.addEvents({
50266         /**
50267          * @event beforeaction
50268          * Fires before any action is performed. Return false to cancel the action.
50269          * @param {Form} this
50270          * @param {Action} action The action to be performed
50271          */
50272         beforeaction: true,
50273         /**
50274          * @event actionfailed
50275          * Fires when an action fails.
50276          * @param {Form} this
50277          * @param {Action} action The action that failed
50278          */
50279         actionfailed : true,
50280         /**
50281          * @event actioncomplete
50282          * Fires when an action is completed.
50283          * @param {Form} this
50284          * @param {Action} action The action that completed
50285          */
50286         actioncomplete : true
50287     });
50288     if(el){
50289         this.initEl(el);
50290     }
50291     Roo.form.BasicForm.superclass.constructor.call(this);
50292     
50293     Roo.form.BasicForm.popover.apply();
50294 };
50295
50296 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50297     /**
50298      * @cfg {String} method
50299      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50300      */
50301     /**
50302      * @cfg {DataReader} reader
50303      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50304      * This is optional as there is built-in support for processing JSON.
50305      */
50306     /**
50307      * @cfg {DataReader} errorReader
50308      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50309      * This is completely optional as there is built-in support for processing JSON.
50310      */
50311     /**
50312      * @cfg {String} url
50313      * The URL to use for form actions if one isn't supplied in the action options.
50314      */
50315     /**
50316      * @cfg {Boolean} fileUpload
50317      * Set to true if this form is a file upload.
50318      */
50319      
50320     /**
50321      * @cfg {Object} baseParams
50322      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50323      */
50324      /**
50325      
50326     /**
50327      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50328      */
50329     timeout: 30,
50330
50331     // private
50332     activeAction : null,
50333
50334     /**
50335      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50336      * or setValues() data instead of when the form was first created.
50337      */
50338     trackResetOnLoad : false,
50339     
50340     
50341     /**
50342      * childForms - used for multi-tab forms
50343      * @type {Array}
50344      */
50345     childForms : false,
50346     
50347     /**
50348      * allItems - full list of fields.
50349      * @type {Array}
50350      */
50351     allItems : false,
50352     
50353     /**
50354      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50355      * element by passing it or its id or mask the form itself by passing in true.
50356      * @type Mixed
50357      */
50358     waitMsgTarget : false,
50359     
50360     /**
50361      * @type Boolean
50362      */
50363     disableMask : false,
50364     
50365     /**
50366      * @cfg {Boolean} errorMask (true|false) default false
50367      */
50368     errorMask : false,
50369     
50370     /**
50371      * @cfg {Number} maskOffset Default 100
50372      */
50373     maskOffset : 100,
50374
50375     // private
50376     initEl : function(el){
50377         this.el = Roo.get(el);
50378         this.id = this.el.id || Roo.id();
50379         this.el.on('submit', this.onSubmit, this);
50380         this.el.addClass('x-form');
50381     },
50382
50383     // private
50384     onSubmit : function(e){
50385         e.stopEvent();
50386     },
50387
50388     /**
50389      * Returns true if client-side validation on the form is successful.
50390      * @return Boolean
50391      */
50392     isValid : function(){
50393         var valid = true;
50394         var target = false;
50395         this.items.each(function(f){
50396             if(f.validate()){
50397                 return;
50398             }
50399             
50400             valid = false;
50401                 
50402             if(!target && f.el.isVisible(true)){
50403                 target = f;
50404             }
50405         });
50406         
50407         if(this.errorMask && !valid){
50408             Roo.form.BasicForm.popover.mask(this, target);
50409         }
50410         
50411         return valid;
50412     },
50413     /**
50414      * Returns array of invalid form fields.
50415      * @return Array
50416      */
50417     
50418     invalidFields : function()
50419     {
50420         var ret = [];
50421         this.items.each(function(f){
50422             if(f.validate()){
50423                 return;
50424             }
50425             ret.push(f);
50426             
50427         });
50428         
50429         return ret;
50430     },
50431     
50432     
50433     /**
50434      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50435      * @return Boolean
50436      */
50437     isDirty : function(){
50438         var dirty = false;
50439         this.items.each(function(f){
50440            if(f.isDirty()){
50441                dirty = true;
50442                return false;
50443            }
50444         });
50445         return dirty;
50446     },
50447     
50448     /**
50449      * Returns true if any fields in this form have changed since their original load. (New version)
50450      * @return Boolean
50451      */
50452     
50453     hasChanged : function()
50454     {
50455         var dirty = false;
50456         this.items.each(function(f){
50457            if(f.hasChanged()){
50458                dirty = true;
50459                return false;
50460            }
50461         });
50462         return dirty;
50463         
50464     },
50465     /**
50466      * Resets all hasChanged to 'false' -
50467      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50468      * So hasChanged storage is only to be used for this purpose
50469      * @return Boolean
50470      */
50471     resetHasChanged : function()
50472     {
50473         this.items.each(function(f){
50474            f.resetHasChanged();
50475         });
50476         
50477     },
50478     
50479     
50480     /**
50481      * Performs a predefined action (submit or load) or custom actions you define on this form.
50482      * @param {String} actionName The name of the action type
50483      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50484      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50485      * accept other config options):
50486      * <pre>
50487 Property          Type             Description
50488 ----------------  ---------------  ----------------------------------------------------------------------------------
50489 url               String           The url for the action (defaults to the form's url)
50490 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50491 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50492 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50493                                    validate the form on the client (defaults to false)
50494      * </pre>
50495      * @return {BasicForm} this
50496      */
50497     doAction : function(action, options){
50498         if(typeof action == 'string'){
50499             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50500         }
50501         if(this.fireEvent('beforeaction', this, action) !== false){
50502             this.beforeAction(action);
50503             action.run.defer(100, action);
50504         }
50505         return this;
50506     },
50507
50508     /**
50509      * Shortcut to do a submit action.
50510      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50511      * @return {BasicForm} this
50512      */
50513     submit : function(options){
50514         this.doAction('submit', options);
50515         return this;
50516     },
50517
50518     /**
50519      * Shortcut to do a load action.
50520      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50521      * @return {BasicForm} this
50522      */
50523     load : function(options){
50524         this.doAction('load', options);
50525         return this;
50526     },
50527
50528     /**
50529      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50530      * @param {Record} record The record to edit
50531      * @return {BasicForm} this
50532      */
50533     updateRecord : function(record){
50534         record.beginEdit();
50535         var fs = record.fields;
50536         fs.each(function(f){
50537             var field = this.findField(f.name);
50538             if(field){
50539                 record.set(f.name, field.getValue());
50540             }
50541         }, this);
50542         record.endEdit();
50543         return this;
50544     },
50545
50546     /**
50547      * Loads an Roo.data.Record into this form.
50548      * @param {Record} record The record to load
50549      * @return {BasicForm} this
50550      */
50551     loadRecord : function(record){
50552         this.setValues(record.data);
50553         return this;
50554     },
50555
50556     // private
50557     beforeAction : function(action){
50558         var o = action.options;
50559         
50560         if(!this.disableMask) {
50561             if(this.waitMsgTarget === true){
50562                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50563             }else if(this.waitMsgTarget){
50564                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50565                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50566             }else {
50567                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50568             }
50569         }
50570         
50571          
50572     },
50573
50574     // private
50575     afterAction : function(action, success){
50576         this.activeAction = null;
50577         var o = action.options;
50578         
50579         if(!this.disableMask) {
50580             if(this.waitMsgTarget === true){
50581                 this.el.unmask();
50582             }else if(this.waitMsgTarget){
50583                 this.waitMsgTarget.unmask();
50584             }else{
50585                 Roo.MessageBox.updateProgress(1);
50586                 Roo.MessageBox.hide();
50587             }
50588         }
50589         
50590         if(success){
50591             if(o.reset){
50592                 this.reset();
50593             }
50594             Roo.callback(o.success, o.scope, [this, action]);
50595             this.fireEvent('actioncomplete', this, action);
50596             
50597         }else{
50598             
50599             // failure condition..
50600             // we have a scenario where updates need confirming.
50601             // eg. if a locking scenario exists..
50602             // we look for { errors : { needs_confirm : true }} in the response.
50603             if (
50604                 (typeof(action.result) != 'undefined')  &&
50605                 (typeof(action.result.errors) != 'undefined')  &&
50606                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50607            ){
50608                 var _t = this;
50609                 Roo.MessageBox.confirm(
50610                     "Change requires confirmation",
50611                     action.result.errorMsg,
50612                     function(r) {
50613                         if (r != 'yes') {
50614                             return;
50615                         }
50616                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50617                     }
50618                     
50619                 );
50620                 
50621                 
50622                 
50623                 return;
50624             }
50625             
50626             Roo.callback(o.failure, o.scope, [this, action]);
50627             // show an error message if no failed handler is set..
50628             if (!this.hasListener('actionfailed')) {
50629                 Roo.MessageBox.alert("Error",
50630                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50631                         action.result.errorMsg :
50632                         "Saving Failed, please check your entries or try again"
50633                 );
50634             }
50635             
50636             this.fireEvent('actionfailed', this, action);
50637         }
50638         
50639     },
50640
50641     /**
50642      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50643      * @param {String} id The value to search for
50644      * @return Field
50645      */
50646     findField : function(id){
50647         var field = this.items.get(id);
50648         if(!field){
50649             this.items.each(function(f){
50650                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50651                     field = f;
50652                     return false;
50653                 }
50654             });
50655         }
50656         return field || null;
50657     },
50658
50659     /**
50660      * Add a secondary form to this one, 
50661      * Used to provide tabbed forms. One form is primary, with hidden values 
50662      * which mirror the elements from the other forms.
50663      * 
50664      * @param {Roo.form.Form} form to add.
50665      * 
50666      */
50667     addForm : function(form)
50668     {
50669        
50670         if (this.childForms.indexOf(form) > -1) {
50671             // already added..
50672             return;
50673         }
50674         this.childForms.push(form);
50675         var n = '';
50676         Roo.each(form.allItems, function (fe) {
50677             
50678             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50679             if (this.findField(n)) { // already added..
50680                 return;
50681             }
50682             var add = new Roo.form.Hidden({
50683                 name : n
50684             });
50685             add.render(this.el);
50686             
50687             this.add( add );
50688         }, this);
50689         
50690     },
50691     /**
50692      * Mark fields in this form invalid in bulk.
50693      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50694      * @return {BasicForm} this
50695      */
50696     markInvalid : function(errors){
50697         if(errors instanceof Array){
50698             for(var i = 0, len = errors.length; i < len; i++){
50699                 var fieldError = errors[i];
50700                 var f = this.findField(fieldError.id);
50701                 if(f){
50702                     f.markInvalid(fieldError.msg);
50703                 }
50704             }
50705         }else{
50706             var field, id;
50707             for(id in errors){
50708                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50709                     field.markInvalid(errors[id]);
50710                 }
50711             }
50712         }
50713         Roo.each(this.childForms || [], function (f) {
50714             f.markInvalid(errors);
50715         });
50716         
50717         return this;
50718     },
50719
50720     /**
50721      * Set values for fields in this form in bulk.
50722      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50723      * @return {BasicForm} this
50724      */
50725     setValues : function(values){
50726         if(values instanceof Array){ // array of objects
50727             for(var i = 0, len = values.length; i < len; i++){
50728                 var v = values[i];
50729                 var f = this.findField(v.id);
50730                 if(f){
50731                     f.setValue(v.value);
50732                     if(this.trackResetOnLoad){
50733                         f.originalValue = f.getValue();
50734                     }
50735                 }
50736             }
50737         }else{ // object hash
50738             var field, id;
50739             for(id in values){
50740                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50741                     
50742                     if (field.setFromData && 
50743                         field.valueField && 
50744                         field.displayField &&
50745                         // combos' with local stores can 
50746                         // be queried via setValue()
50747                         // to set their value..
50748                         (field.store && !field.store.isLocal)
50749                         ) {
50750                         // it's a combo
50751                         var sd = { };
50752                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50753                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50754                         field.setFromData(sd);
50755                         
50756                     } else {
50757                         field.setValue(values[id]);
50758                     }
50759                     
50760                     
50761                     if(this.trackResetOnLoad){
50762                         field.originalValue = field.getValue();
50763                     }
50764                 }
50765             }
50766         }
50767         this.resetHasChanged();
50768         
50769         
50770         Roo.each(this.childForms || [], function (f) {
50771             f.setValues(values);
50772             f.resetHasChanged();
50773         });
50774                 
50775         return this;
50776     },
50777  
50778     /**
50779      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50780      * they are returned as an array.
50781      * @param {Boolean} asString
50782      * @return {Object}
50783      */
50784     getValues : function(asString)
50785     {
50786         if (this.childForms) {
50787             // copy values from the child forms
50788             Roo.each(this.childForms, function (f) {
50789                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50790             }, this);
50791         }
50792         
50793         // use formdata
50794         if (typeof(FormData) != 'undefined' && asString !== true) {
50795             // this relies on a 'recent' version of chrome apparently...
50796             try {
50797                 var fd = (new FormData(this.el.dom)).entries();
50798                 var ret = {};
50799                 var ent = fd.next();
50800                 while (!ent.done) {
50801                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50802                     ent = fd.next();
50803                 };
50804                 return ret;
50805             } catch(e) {
50806                 
50807             }
50808             
50809         }
50810         
50811         
50812         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50813         if(asString === true){
50814             return fs;
50815         }
50816         return Roo.urlDecode(fs);
50817     },
50818     
50819     /**
50820      * Returns the fields in this form as an object with key/value pairs. 
50821      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50822      * @return {Object}
50823      */
50824     getFieldValues : function(with_hidden)
50825     {
50826         if (this.childForms) {
50827             // copy values from the child forms
50828             // should this call getFieldValues - probably not as we do not currently copy
50829             // hidden fields when we generate..
50830             Roo.each(this.childForms, function (f) {
50831                 this.setValues(f.getFieldValues());
50832             }, this);
50833         }
50834         
50835         var ret = {};
50836         this.items.each(function(f){
50837             
50838             if (f.readOnly) {
50839                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50840                         // if a subform contains a copy of them.
50841                         // if you have subforms with the same editable data, you will need to copy the data back
50842                         // and forth.
50843             }
50844             
50845             if (!f.getName()) {
50846                 return;
50847             }
50848             var v = f.getValue();
50849             if (f.inputType =='radio') {
50850                 if (typeof(ret[f.getName()]) == 'undefined') {
50851                     ret[f.getName()] = ''; // empty..
50852                 }
50853                 
50854                 if (!f.el.dom.checked) {
50855                     return;
50856                     
50857                 }
50858                 v = f.el.dom.value;
50859                 
50860             }
50861             
50862             // not sure if this supported any more..
50863             if ((typeof(v) == 'object') && f.getRawValue) {
50864                 v = f.getRawValue() ; // dates..
50865             }
50866             // combo boxes where name != hiddenName...
50867             if (f.name != f.getName()) {
50868                 ret[f.name] = f.getRawValue();
50869             }
50870             ret[f.getName()] = v;
50871         });
50872         
50873         return ret;
50874     },
50875
50876     /**
50877      * Clears all invalid messages in this form.
50878      * @return {BasicForm} this
50879      */
50880     clearInvalid : function(){
50881         this.items.each(function(f){
50882            f.clearInvalid();
50883         });
50884         
50885         Roo.each(this.childForms || [], function (f) {
50886             f.clearInvalid();
50887         });
50888         
50889         
50890         return this;
50891     },
50892
50893     /**
50894      * Resets this form.
50895      * @return {BasicForm} this
50896      */
50897     reset : function(){
50898         this.items.each(function(f){
50899             f.reset();
50900         });
50901         
50902         Roo.each(this.childForms || [], function (f) {
50903             f.reset();
50904         });
50905         this.resetHasChanged();
50906         
50907         return this;
50908     },
50909
50910     /**
50911      * Add Roo.form components to this form.
50912      * @param {Field} field1
50913      * @param {Field} field2 (optional)
50914      * @param {Field} etc (optional)
50915      * @return {BasicForm} this
50916      */
50917     add : function(){
50918         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50919         return this;
50920     },
50921
50922
50923     /**
50924      * Removes a field from the items collection (does NOT remove its markup).
50925      * @param {Field} field
50926      * @return {BasicForm} this
50927      */
50928     remove : function(field){
50929         this.items.remove(field);
50930         return this;
50931     },
50932
50933     /**
50934      * Looks at the fields in this form, checks them for an id attribute,
50935      * and calls applyTo on the existing dom element with that id.
50936      * @return {BasicForm} this
50937      */
50938     render : function(){
50939         this.items.each(function(f){
50940             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50941                 f.applyTo(f.id);
50942             }
50943         });
50944         return this;
50945     },
50946
50947     /**
50948      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50949      * @param {Object} values
50950      * @return {BasicForm} this
50951      */
50952     applyToFields : function(o){
50953         this.items.each(function(f){
50954            Roo.apply(f, o);
50955         });
50956         return this;
50957     },
50958
50959     /**
50960      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50961      * @param {Object} values
50962      * @return {BasicForm} this
50963      */
50964     applyIfToFields : function(o){
50965         this.items.each(function(f){
50966            Roo.applyIf(f, o);
50967         });
50968         return this;
50969     }
50970 });
50971
50972 // back compat
50973 Roo.BasicForm = Roo.form.BasicForm;
50974
50975 Roo.apply(Roo.form.BasicForm, {
50976     
50977     popover : {
50978         
50979         padding : 5,
50980         
50981         isApplied : false,
50982         
50983         isMasked : false,
50984         
50985         form : false,
50986         
50987         target : false,
50988         
50989         intervalID : false,
50990         
50991         maskEl : false,
50992         
50993         apply : function()
50994         {
50995             if(this.isApplied){
50996                 return;
50997             }
50998             
50999             this.maskEl = {
51000                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51001                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51002                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51003                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51004             };
51005             
51006             this.maskEl.top.enableDisplayMode("block");
51007             this.maskEl.left.enableDisplayMode("block");
51008             this.maskEl.bottom.enableDisplayMode("block");
51009             this.maskEl.right.enableDisplayMode("block");
51010             
51011             Roo.get(document.body).on('click', function(){
51012                 this.unmask();
51013             }, this);
51014             
51015             Roo.get(document.body).on('touchstart', function(){
51016                 this.unmask();
51017             }, this);
51018             
51019             this.isApplied = true
51020         },
51021         
51022         mask : function(form, target)
51023         {
51024             this.form = form;
51025             
51026             this.target = target;
51027             
51028             if(!this.form.errorMask || !target.el){
51029                 return;
51030             }
51031             
51032             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51033             
51034             var ot = this.target.el.calcOffsetsTo(scrollable);
51035             
51036             var scrollTo = ot[1] - this.form.maskOffset;
51037             
51038             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51039             
51040             scrollable.scrollTo('top', scrollTo);
51041             
51042             var el = this.target.wrap || this.target.el;
51043             
51044             var box = el.getBox();
51045             
51046             this.maskEl.top.setStyle('position', 'absolute');
51047             this.maskEl.top.setStyle('z-index', 10000);
51048             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51049             this.maskEl.top.setLeft(0);
51050             this.maskEl.top.setTop(0);
51051             this.maskEl.top.show();
51052             
51053             this.maskEl.left.setStyle('position', 'absolute');
51054             this.maskEl.left.setStyle('z-index', 10000);
51055             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51056             this.maskEl.left.setLeft(0);
51057             this.maskEl.left.setTop(box.y - this.padding);
51058             this.maskEl.left.show();
51059
51060             this.maskEl.bottom.setStyle('position', 'absolute');
51061             this.maskEl.bottom.setStyle('z-index', 10000);
51062             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51063             this.maskEl.bottom.setLeft(0);
51064             this.maskEl.bottom.setTop(box.bottom + this.padding);
51065             this.maskEl.bottom.show();
51066
51067             this.maskEl.right.setStyle('position', 'absolute');
51068             this.maskEl.right.setStyle('z-index', 10000);
51069             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51070             this.maskEl.right.setLeft(box.right + this.padding);
51071             this.maskEl.right.setTop(box.y - this.padding);
51072             this.maskEl.right.show();
51073
51074             this.intervalID = window.setInterval(function() {
51075                 Roo.form.BasicForm.popover.unmask();
51076             }, 10000);
51077
51078             window.onwheel = function(){ return false;};
51079             
51080             (function(){ this.isMasked = true; }).defer(500, this);
51081             
51082         },
51083         
51084         unmask : function()
51085         {
51086             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51087                 return;
51088             }
51089             
51090             this.maskEl.top.setStyle('position', 'absolute');
51091             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51092             this.maskEl.top.hide();
51093
51094             this.maskEl.left.setStyle('position', 'absolute');
51095             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51096             this.maskEl.left.hide();
51097
51098             this.maskEl.bottom.setStyle('position', 'absolute');
51099             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51100             this.maskEl.bottom.hide();
51101
51102             this.maskEl.right.setStyle('position', 'absolute');
51103             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51104             this.maskEl.right.hide();
51105             
51106             window.onwheel = function(){ return true;};
51107             
51108             if(this.intervalID){
51109                 window.clearInterval(this.intervalID);
51110                 this.intervalID = false;
51111             }
51112             
51113             this.isMasked = false;
51114             
51115         }
51116         
51117     }
51118     
51119 });/*
51120  * Based on:
51121  * Ext JS Library 1.1.1
51122  * Copyright(c) 2006-2007, Ext JS, LLC.
51123  *
51124  * Originally Released Under LGPL - original licence link has changed is not relivant.
51125  *
51126  * Fork - LGPL
51127  * <script type="text/javascript">
51128  */
51129
51130 /**
51131  * @class Roo.form.Form
51132  * @extends Roo.form.BasicForm
51133  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51134  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51135  * @constructor
51136  * @param {Object} config Configuration options
51137  */
51138 Roo.form.Form = function(config){
51139     var xitems =  [];
51140     if (config.items) {
51141         xitems = config.items;
51142         delete config.items;
51143     }
51144    
51145     
51146     Roo.form.Form.superclass.constructor.call(this, null, config);
51147     this.url = this.url || this.action;
51148     if(!this.root){
51149         this.root = new Roo.form.Layout(Roo.applyIf({
51150             id: Roo.id()
51151         }, config));
51152     }
51153     this.active = this.root;
51154     /**
51155      * Array of all the buttons that have been added to this form via {@link addButton}
51156      * @type Array
51157      */
51158     this.buttons = [];
51159     this.allItems = [];
51160     this.addEvents({
51161         /**
51162          * @event clientvalidation
51163          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51164          * @param {Form} this
51165          * @param {Boolean} valid true if the form has passed client-side validation
51166          */
51167         clientvalidation: true,
51168         /**
51169          * @event rendered
51170          * Fires when the form is rendered
51171          * @param {Roo.form.Form} form
51172          */
51173         rendered : true
51174     });
51175     
51176     if (this.progressUrl) {
51177             // push a hidden field onto the list of fields..
51178             this.addxtype( {
51179                     xns: Roo.form, 
51180                     xtype : 'Hidden', 
51181                     name : 'UPLOAD_IDENTIFIER' 
51182             });
51183         }
51184         
51185     
51186     Roo.each(xitems, this.addxtype, this);
51187     
51188 };
51189
51190 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51191      /**
51192      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51193      */
51194     
51195     /**
51196      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51197      */
51198     /**
51199      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51200      */
51201     /**
51202      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51203      */
51204     buttonAlign:'center',
51205
51206     /**
51207      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51208      */
51209     minButtonWidth:75,
51210
51211     /**
51212      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51213      * This property cascades to child containers if not set.
51214      */
51215     labelAlign:'left',
51216
51217     /**
51218      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51219      * fires a looping event with that state. This is required to bind buttons to the valid
51220      * state using the config value formBind:true on the button.
51221      */
51222     monitorValid : false,
51223
51224     /**
51225      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51226      */
51227     monitorPoll : 200,
51228     
51229     /**
51230      * @cfg {String} progressUrl - Url to return progress data 
51231      */
51232     
51233     progressUrl : false,
51234     /**
51235      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51236      * sending a formdata with extra parameters - eg uploaded elements.
51237      */
51238     
51239     formData : false,
51240     
51241     /**
51242      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51243      * fields are added and the column is closed. If no fields are passed the column remains open
51244      * until end() is called.
51245      * @param {Object} config The config to pass to the column
51246      * @param {Field} field1 (optional)
51247      * @param {Field} field2 (optional)
51248      * @param {Field} etc (optional)
51249      * @return Column The column container object
51250      */
51251     column : function(c){
51252         var col = new Roo.form.Column(c);
51253         this.start(col);
51254         if(arguments.length > 1){ // duplicate code required because of Opera
51255             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51256             this.end();
51257         }
51258         return col;
51259     },
51260
51261     /**
51262      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51263      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51264      * until end() is called.
51265      * @param {Object} config The config to pass to the fieldset
51266      * @param {Field} field1 (optional)
51267      * @param {Field} field2 (optional)
51268      * @param {Field} etc (optional)
51269      * @return FieldSet The fieldset container object
51270      */
51271     fieldset : function(c){
51272         var fs = new Roo.form.FieldSet(c);
51273         this.start(fs);
51274         if(arguments.length > 1){ // duplicate code required because of Opera
51275             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51276             this.end();
51277         }
51278         return fs;
51279     },
51280
51281     /**
51282      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51283      * fields are added and the container is closed. If no fields are passed the container remains open
51284      * until end() is called.
51285      * @param {Object} config The config to pass to the Layout
51286      * @param {Field} field1 (optional)
51287      * @param {Field} field2 (optional)
51288      * @param {Field} etc (optional)
51289      * @return Layout The container object
51290      */
51291     container : function(c){
51292         var l = new Roo.form.Layout(c);
51293         this.start(l);
51294         if(arguments.length > 1){ // duplicate code required because of Opera
51295             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51296             this.end();
51297         }
51298         return l;
51299     },
51300
51301     /**
51302      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51303      * @param {Object} container A Roo.form.Layout or subclass of Layout
51304      * @return {Form} this
51305      */
51306     start : function(c){
51307         // cascade label info
51308         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51309         this.active.stack.push(c);
51310         c.ownerCt = this.active;
51311         this.active = c;
51312         return this;
51313     },
51314
51315     /**
51316      * Closes the current open container
51317      * @return {Form} this
51318      */
51319     end : function(){
51320         if(this.active == this.root){
51321             return this;
51322         }
51323         this.active = this.active.ownerCt;
51324         return this;
51325     },
51326
51327     /**
51328      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51329      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51330      * as the label of the field.
51331      * @param {Field} field1
51332      * @param {Field} field2 (optional)
51333      * @param {Field} etc. (optional)
51334      * @return {Form} this
51335      */
51336     add : function(){
51337         this.active.stack.push.apply(this.active.stack, arguments);
51338         this.allItems.push.apply(this.allItems,arguments);
51339         var r = [];
51340         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51341             if(a[i].isFormField){
51342                 r.push(a[i]);
51343             }
51344         }
51345         if(r.length > 0){
51346             Roo.form.Form.superclass.add.apply(this, r);
51347         }
51348         return this;
51349     },
51350     
51351
51352     
51353     
51354     
51355      /**
51356      * Find any element that has been added to a form, using it's ID or name
51357      * This can include framesets, columns etc. along with regular fields..
51358      * @param {String} id - id or name to find.
51359      
51360      * @return {Element} e - or false if nothing found.
51361      */
51362     findbyId : function(id)
51363     {
51364         var ret = false;
51365         if (!id) {
51366             return ret;
51367         }
51368         Roo.each(this.allItems, function(f){
51369             if (f.id == id || f.name == id ){
51370                 ret = f;
51371                 return false;
51372             }
51373         });
51374         return ret;
51375     },
51376
51377     
51378     
51379     /**
51380      * Render this form into the passed container. This should only be called once!
51381      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51382      * @return {Form} this
51383      */
51384     render : function(ct)
51385     {
51386         
51387         
51388         
51389         ct = Roo.get(ct);
51390         var o = this.autoCreate || {
51391             tag: 'form',
51392             method : this.method || 'POST',
51393             id : this.id || Roo.id()
51394         };
51395         this.initEl(ct.createChild(o));
51396
51397         this.root.render(this.el);
51398         
51399        
51400              
51401         this.items.each(function(f){
51402             f.render('x-form-el-'+f.id);
51403         });
51404
51405         if(this.buttons.length > 0){
51406             // tables are required to maintain order and for correct IE layout
51407             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51408                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51409                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51410             }}, null, true);
51411             var tr = tb.getElementsByTagName('tr')[0];
51412             for(var i = 0, len = this.buttons.length; i < len; i++) {
51413                 var b = this.buttons[i];
51414                 var td = document.createElement('td');
51415                 td.className = 'x-form-btn-td';
51416                 b.render(tr.appendChild(td));
51417             }
51418         }
51419         if(this.monitorValid){ // initialize after render
51420             this.startMonitoring();
51421         }
51422         this.fireEvent('rendered', this);
51423         return this;
51424     },
51425
51426     /**
51427      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51428      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51429      * object or a valid Roo.DomHelper element config
51430      * @param {Function} handler The function called when the button is clicked
51431      * @param {Object} scope (optional) The scope of the handler function
51432      * @return {Roo.Button}
51433      */
51434     addButton : function(config, handler, scope){
51435         var bc = {
51436             handler: handler,
51437             scope: scope,
51438             minWidth: this.minButtonWidth,
51439             hideParent:true
51440         };
51441         if(typeof config == "string"){
51442             bc.text = config;
51443         }else{
51444             Roo.apply(bc, config);
51445         }
51446         var btn = new Roo.Button(null, bc);
51447         this.buttons.push(btn);
51448         return btn;
51449     },
51450
51451      /**
51452      * Adds a series of form elements (using the xtype property as the factory method.
51453      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51454      * @param {Object} config 
51455      */
51456     
51457     addxtype : function()
51458     {
51459         var ar = Array.prototype.slice.call(arguments, 0);
51460         var ret = false;
51461         for(var i = 0; i < ar.length; i++) {
51462             if (!ar[i]) {
51463                 continue; // skip -- if this happends something invalid got sent, we 
51464                 // should ignore it, as basically that interface element will not show up
51465                 // and that should be pretty obvious!!
51466             }
51467             
51468             if (Roo.form[ar[i].xtype]) {
51469                 ar[i].form = this;
51470                 var fe = Roo.factory(ar[i], Roo.form);
51471                 if (!ret) {
51472                     ret = fe;
51473                 }
51474                 fe.form = this;
51475                 if (fe.store) {
51476                     fe.store.form = this;
51477                 }
51478                 if (fe.isLayout) {  
51479                          
51480                     this.start(fe);
51481                     this.allItems.push(fe);
51482                     if (fe.items && fe.addxtype) {
51483                         fe.addxtype.apply(fe, fe.items);
51484                         delete fe.items;
51485                     }
51486                      this.end();
51487                     continue;
51488                 }
51489                 
51490                 
51491                  
51492                 this.add(fe);
51493               //  console.log('adding ' + ar[i].xtype);
51494             }
51495             if (ar[i].xtype == 'Button') {  
51496                 //console.log('adding button');
51497                 //console.log(ar[i]);
51498                 this.addButton(ar[i]);
51499                 this.allItems.push(fe);
51500                 continue;
51501             }
51502             
51503             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51504                 alert('end is not supported on xtype any more, use items');
51505             //    this.end();
51506             //    //console.log('adding end');
51507             }
51508             
51509         }
51510         return ret;
51511     },
51512     
51513     /**
51514      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51515      * option "monitorValid"
51516      */
51517     startMonitoring : function(){
51518         if(!this.bound){
51519             this.bound = true;
51520             Roo.TaskMgr.start({
51521                 run : this.bindHandler,
51522                 interval : this.monitorPoll || 200,
51523                 scope: this
51524             });
51525         }
51526     },
51527
51528     /**
51529      * Stops monitoring of the valid state of this form
51530      */
51531     stopMonitoring : function(){
51532         this.bound = false;
51533     },
51534
51535     // private
51536     bindHandler : function(){
51537         if(!this.bound){
51538             return false; // stops binding
51539         }
51540         var valid = true;
51541         this.items.each(function(f){
51542             if(!f.isValid(true)){
51543                 valid = false;
51544                 return false;
51545             }
51546         });
51547         for(var i = 0, len = this.buttons.length; i < len; i++){
51548             var btn = this.buttons[i];
51549             if(btn.formBind === true && btn.disabled === valid){
51550                 btn.setDisabled(!valid);
51551             }
51552         }
51553         this.fireEvent('clientvalidation', this, valid);
51554     }
51555     
51556     
51557     
51558     
51559     
51560     
51561     
51562     
51563 });
51564
51565
51566 // back compat
51567 Roo.Form = Roo.form.Form;
51568 /*
51569  * Based on:
51570  * Ext JS Library 1.1.1
51571  * Copyright(c) 2006-2007, Ext JS, LLC.
51572  *
51573  * Originally Released Under LGPL - original licence link has changed is not relivant.
51574  *
51575  * Fork - LGPL
51576  * <script type="text/javascript">
51577  */
51578
51579 // as we use this in bootstrap.
51580 Roo.namespace('Roo.form');
51581  /**
51582  * @class Roo.form.Action
51583  * Internal Class used to handle form actions
51584  * @constructor
51585  * @param {Roo.form.BasicForm} el The form element or its id
51586  * @param {Object} config Configuration options
51587  */
51588
51589  
51590  
51591 // define the action interface
51592 Roo.form.Action = function(form, options){
51593     this.form = form;
51594     this.options = options || {};
51595 };
51596 /**
51597  * Client Validation Failed
51598  * @const 
51599  */
51600 Roo.form.Action.CLIENT_INVALID = 'client';
51601 /**
51602  * Server Validation Failed
51603  * @const 
51604  */
51605 Roo.form.Action.SERVER_INVALID = 'server';
51606  /**
51607  * Connect to Server Failed
51608  * @const 
51609  */
51610 Roo.form.Action.CONNECT_FAILURE = 'connect';
51611 /**
51612  * Reading Data from Server Failed
51613  * @const 
51614  */
51615 Roo.form.Action.LOAD_FAILURE = 'load';
51616
51617 Roo.form.Action.prototype = {
51618     type : 'default',
51619     failureType : undefined,
51620     response : undefined,
51621     result : undefined,
51622
51623     // interface method
51624     run : function(options){
51625
51626     },
51627
51628     // interface method
51629     success : function(response){
51630
51631     },
51632
51633     // interface method
51634     handleResponse : function(response){
51635
51636     },
51637
51638     // default connection failure
51639     failure : function(response){
51640         
51641         this.response = response;
51642         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51643         this.form.afterAction(this, false);
51644     },
51645
51646     processResponse : function(response){
51647         this.response = response;
51648         if(!response.responseText){
51649             return true;
51650         }
51651         this.result = this.handleResponse(response);
51652         return this.result;
51653     },
51654
51655     // utility functions used internally
51656     getUrl : function(appendParams){
51657         var url = this.options.url || this.form.url || this.form.el.dom.action;
51658         if(appendParams){
51659             var p = this.getParams();
51660             if(p){
51661                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51662             }
51663         }
51664         return url;
51665     },
51666
51667     getMethod : function(){
51668         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51669     },
51670
51671     getParams : function(){
51672         var bp = this.form.baseParams;
51673         var p = this.options.params;
51674         if(p){
51675             if(typeof p == "object"){
51676                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51677             }else if(typeof p == 'string' && bp){
51678                 p += '&' + Roo.urlEncode(bp);
51679             }
51680         }else if(bp){
51681             p = Roo.urlEncode(bp);
51682         }
51683         return p;
51684     },
51685
51686     createCallback : function(){
51687         return {
51688             success: this.success,
51689             failure: this.failure,
51690             scope: this,
51691             timeout: (this.form.timeout*1000),
51692             upload: this.form.fileUpload ? this.success : undefined
51693         };
51694     }
51695 };
51696
51697 Roo.form.Action.Submit = function(form, options){
51698     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51699 };
51700
51701 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51702     type : 'submit',
51703
51704     haveProgress : false,
51705     uploadComplete : false,
51706     
51707     // uploadProgress indicator.
51708     uploadProgress : function()
51709     {
51710         if (!this.form.progressUrl) {
51711             return;
51712         }
51713         
51714         if (!this.haveProgress) {
51715             Roo.MessageBox.progress("Uploading", "Uploading");
51716         }
51717         if (this.uploadComplete) {
51718            Roo.MessageBox.hide();
51719            return;
51720         }
51721         
51722         this.haveProgress = true;
51723    
51724         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51725         
51726         var c = new Roo.data.Connection();
51727         c.request({
51728             url : this.form.progressUrl,
51729             params: {
51730                 id : uid
51731             },
51732             method: 'GET',
51733             success : function(req){
51734                //console.log(data);
51735                 var rdata = false;
51736                 var edata;
51737                 try  {
51738                    rdata = Roo.decode(req.responseText)
51739                 } catch (e) {
51740                     Roo.log("Invalid data from server..");
51741                     Roo.log(edata);
51742                     return;
51743                 }
51744                 if (!rdata || !rdata.success) {
51745                     Roo.log(rdata);
51746                     Roo.MessageBox.alert(Roo.encode(rdata));
51747                     return;
51748                 }
51749                 var data = rdata.data;
51750                 
51751                 if (this.uploadComplete) {
51752                    Roo.MessageBox.hide();
51753                    return;
51754                 }
51755                    
51756                 if (data){
51757                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51758                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51759                     );
51760                 }
51761                 this.uploadProgress.defer(2000,this);
51762             },
51763        
51764             failure: function(data) {
51765                 Roo.log('progress url failed ');
51766                 Roo.log(data);
51767             },
51768             scope : this
51769         });
51770            
51771     },
51772     
51773     
51774     run : function()
51775     {
51776         // run get Values on the form, so it syncs any secondary forms.
51777         this.form.getValues();
51778         
51779         var o = this.options;
51780         var method = this.getMethod();
51781         var isPost = method == 'POST';
51782         if(o.clientValidation === false || this.form.isValid()){
51783             
51784             if (this.form.progressUrl) {
51785                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51786                     (new Date() * 1) + '' + Math.random());
51787                     
51788             } 
51789             
51790             
51791             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51792                 form:this.form.el.dom,
51793                 url:this.getUrl(!isPost),
51794                 method: method,
51795                 params:isPost ? this.getParams() : null,
51796                 isUpload: this.form.fileUpload,
51797                 formData : this.form.formData
51798             }));
51799             
51800             this.uploadProgress();
51801
51802         }else if (o.clientValidation !== false){ // client validation failed
51803             this.failureType = Roo.form.Action.CLIENT_INVALID;
51804             this.form.afterAction(this, false);
51805         }
51806     },
51807
51808     success : function(response)
51809     {
51810         this.uploadComplete= true;
51811         if (this.haveProgress) {
51812             Roo.MessageBox.hide();
51813         }
51814         
51815         
51816         var result = this.processResponse(response);
51817         if(result === true || result.success){
51818             this.form.afterAction(this, true);
51819             return;
51820         }
51821         if(result.errors){
51822             this.form.markInvalid(result.errors);
51823             this.failureType = Roo.form.Action.SERVER_INVALID;
51824         }
51825         this.form.afterAction(this, false);
51826     },
51827     failure : function(response)
51828     {
51829         this.uploadComplete= true;
51830         if (this.haveProgress) {
51831             Roo.MessageBox.hide();
51832         }
51833         
51834         this.response = response;
51835         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51836         this.form.afterAction(this, false);
51837     },
51838     
51839     handleResponse : function(response){
51840         if(this.form.errorReader){
51841             var rs = this.form.errorReader.read(response);
51842             var errors = [];
51843             if(rs.records){
51844                 for(var i = 0, len = rs.records.length; i < len; i++) {
51845                     var r = rs.records[i];
51846                     errors[i] = r.data;
51847                 }
51848             }
51849             if(errors.length < 1){
51850                 errors = null;
51851             }
51852             return {
51853                 success : rs.success,
51854                 errors : errors
51855             };
51856         }
51857         var ret = false;
51858         try {
51859             ret = Roo.decode(response.responseText);
51860         } catch (e) {
51861             ret = {
51862                 success: false,
51863                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51864                 errors : []
51865             };
51866         }
51867         return ret;
51868         
51869     }
51870 });
51871
51872
51873 Roo.form.Action.Load = function(form, options){
51874     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51875     this.reader = this.form.reader;
51876 };
51877
51878 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51879     type : 'load',
51880
51881     run : function(){
51882         
51883         Roo.Ajax.request(Roo.apply(
51884                 this.createCallback(), {
51885                     method:this.getMethod(),
51886                     url:this.getUrl(false),
51887                     params:this.getParams()
51888         }));
51889     },
51890
51891     success : function(response){
51892         
51893         var result = this.processResponse(response);
51894         if(result === true || !result.success || !result.data){
51895             this.failureType = Roo.form.Action.LOAD_FAILURE;
51896             this.form.afterAction(this, false);
51897             return;
51898         }
51899         this.form.clearInvalid();
51900         this.form.setValues(result.data);
51901         this.form.afterAction(this, true);
51902     },
51903
51904     handleResponse : function(response){
51905         if(this.form.reader){
51906             var rs = this.form.reader.read(response);
51907             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51908             return {
51909                 success : rs.success,
51910                 data : data
51911             };
51912         }
51913         return Roo.decode(response.responseText);
51914     }
51915 });
51916
51917 Roo.form.Action.ACTION_TYPES = {
51918     'load' : Roo.form.Action.Load,
51919     'submit' : Roo.form.Action.Submit
51920 };/*
51921  * Based on:
51922  * Ext JS Library 1.1.1
51923  * Copyright(c) 2006-2007, Ext JS, LLC.
51924  *
51925  * Originally Released Under LGPL - original licence link has changed is not relivant.
51926  *
51927  * Fork - LGPL
51928  * <script type="text/javascript">
51929  */
51930  
51931 /**
51932  * @class Roo.form.Layout
51933  * @extends Roo.Component
51934  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51935  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51936  * @constructor
51937  * @param {Object} config Configuration options
51938  */
51939 Roo.form.Layout = function(config){
51940     var xitems = [];
51941     if (config.items) {
51942         xitems = config.items;
51943         delete config.items;
51944     }
51945     Roo.form.Layout.superclass.constructor.call(this, config);
51946     this.stack = [];
51947     Roo.each(xitems, this.addxtype, this);
51948      
51949 };
51950
51951 Roo.extend(Roo.form.Layout, Roo.Component, {
51952     /**
51953      * @cfg {String/Object} autoCreate
51954      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51955      */
51956     /**
51957      * @cfg {String/Object/Function} style
51958      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51959      * a function which returns such a specification.
51960      */
51961     /**
51962      * @cfg {String} labelAlign
51963      * Valid values are "left," "top" and "right" (defaults to "left")
51964      */
51965     /**
51966      * @cfg {Number} labelWidth
51967      * Fixed width in pixels of all field labels (defaults to undefined)
51968      */
51969     /**
51970      * @cfg {Boolean} clear
51971      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51972      */
51973     clear : true,
51974     /**
51975      * @cfg {String} labelSeparator
51976      * The separator to use after field labels (defaults to ':')
51977      */
51978     labelSeparator : ':',
51979     /**
51980      * @cfg {Boolean} hideLabels
51981      * True to suppress the display of field labels in this layout (defaults to false)
51982      */
51983     hideLabels : false,
51984
51985     // private
51986     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51987     
51988     isLayout : true,
51989     
51990     // private
51991     onRender : function(ct, position){
51992         if(this.el){ // from markup
51993             this.el = Roo.get(this.el);
51994         }else {  // generate
51995             var cfg = this.getAutoCreate();
51996             this.el = ct.createChild(cfg, position);
51997         }
51998         if(this.style){
51999             this.el.applyStyles(this.style);
52000         }
52001         if(this.labelAlign){
52002             this.el.addClass('x-form-label-'+this.labelAlign);
52003         }
52004         if(this.hideLabels){
52005             this.labelStyle = "display:none";
52006             this.elementStyle = "padding-left:0;";
52007         }else{
52008             if(typeof this.labelWidth == 'number'){
52009                 this.labelStyle = "width:"+this.labelWidth+"px;";
52010                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52011             }
52012             if(this.labelAlign == 'top'){
52013                 this.labelStyle = "width:auto;";
52014                 this.elementStyle = "padding-left:0;";
52015             }
52016         }
52017         var stack = this.stack;
52018         var slen = stack.length;
52019         if(slen > 0){
52020             if(!this.fieldTpl){
52021                 var t = new Roo.Template(
52022                     '<div class="x-form-item {5}">',
52023                         '<label for="{0}" style="{2}">{1}{4}</label>',
52024                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52025                         '</div>',
52026                     '</div><div class="x-form-clear-left"></div>'
52027                 );
52028                 t.disableFormats = true;
52029                 t.compile();
52030                 Roo.form.Layout.prototype.fieldTpl = t;
52031             }
52032             for(var i = 0; i < slen; i++) {
52033                 if(stack[i].isFormField){
52034                     this.renderField(stack[i]);
52035                 }else{
52036                     this.renderComponent(stack[i]);
52037                 }
52038             }
52039         }
52040         if(this.clear){
52041             this.el.createChild({cls:'x-form-clear'});
52042         }
52043     },
52044
52045     // private
52046     renderField : function(f){
52047         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52048                f.id, //0
52049                f.fieldLabel, //1
52050                f.labelStyle||this.labelStyle||'', //2
52051                this.elementStyle||'', //3
52052                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52053                f.itemCls||this.itemCls||''  //5
52054        ], true).getPrevSibling());
52055     },
52056
52057     // private
52058     renderComponent : function(c){
52059         c.render(c.isLayout ? this.el : this.el.createChild());    
52060     },
52061     /**
52062      * Adds a object form elements (using the xtype property as the factory method.)
52063      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52064      * @param {Object} config 
52065      */
52066     addxtype : function(o)
52067     {
52068         // create the lement.
52069         o.form = this.form;
52070         var fe = Roo.factory(o, Roo.form);
52071         this.form.allItems.push(fe);
52072         this.stack.push(fe);
52073         
52074         if (fe.isFormField) {
52075             this.form.items.add(fe);
52076         }
52077          
52078         return fe;
52079     }
52080 });
52081
52082 /**
52083  * @class Roo.form.Column
52084  * @extends Roo.form.Layout
52085  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52086  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52087  * @constructor
52088  * @param {Object} config Configuration options
52089  */
52090 Roo.form.Column = function(config){
52091     Roo.form.Column.superclass.constructor.call(this, config);
52092 };
52093
52094 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52095     /**
52096      * @cfg {Number/String} width
52097      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52098      */
52099     /**
52100      * @cfg {String/Object} autoCreate
52101      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52102      */
52103
52104     // private
52105     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52106
52107     // private
52108     onRender : function(ct, position){
52109         Roo.form.Column.superclass.onRender.call(this, ct, position);
52110         if(this.width){
52111             this.el.setWidth(this.width);
52112         }
52113     }
52114 });
52115
52116
52117 /**
52118  * @class Roo.form.Row
52119  * @extends Roo.form.Layout
52120  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52121  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52122  * @constructor
52123  * @param {Object} config Configuration options
52124  */
52125
52126  
52127 Roo.form.Row = function(config){
52128     Roo.form.Row.superclass.constructor.call(this, config);
52129 };
52130  
52131 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52132       /**
52133      * @cfg {Number/String} width
52134      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52135      */
52136     /**
52137      * @cfg {Number/String} height
52138      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52139      */
52140     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52141     
52142     padWidth : 20,
52143     // private
52144     onRender : function(ct, position){
52145         //console.log('row render');
52146         if(!this.rowTpl){
52147             var t = new Roo.Template(
52148                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52149                     '<label for="{0}" style="{2}">{1}{4}</label>',
52150                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52151                     '</div>',
52152                 '</div>'
52153             );
52154             t.disableFormats = true;
52155             t.compile();
52156             Roo.form.Layout.prototype.rowTpl = t;
52157         }
52158         this.fieldTpl = this.rowTpl;
52159         
52160         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52161         var labelWidth = 100;
52162         
52163         if ((this.labelAlign != 'top')) {
52164             if (typeof this.labelWidth == 'number') {
52165                 labelWidth = this.labelWidth
52166             }
52167             this.padWidth =  20 + labelWidth;
52168             
52169         }
52170         
52171         Roo.form.Column.superclass.onRender.call(this, ct, position);
52172         if(this.width){
52173             this.el.setWidth(this.width);
52174         }
52175         if(this.height){
52176             this.el.setHeight(this.height);
52177         }
52178     },
52179     
52180     // private
52181     renderField : function(f){
52182         f.fieldEl = this.fieldTpl.append(this.el, [
52183                f.id, f.fieldLabel,
52184                f.labelStyle||this.labelStyle||'',
52185                this.elementStyle||'',
52186                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52187                f.itemCls||this.itemCls||'',
52188                f.width ? f.width + this.padWidth : 160 + this.padWidth
52189        ],true);
52190     }
52191 });
52192  
52193
52194 /**
52195  * @class Roo.form.FieldSet
52196  * @extends Roo.form.Layout
52197  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52198  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52199  * @constructor
52200  * @param {Object} config Configuration options
52201  */
52202 Roo.form.FieldSet = function(config){
52203     Roo.form.FieldSet.superclass.constructor.call(this, config);
52204 };
52205
52206 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52207     /**
52208      * @cfg {String} legend
52209      * The text to display as the legend for the FieldSet (defaults to '')
52210      */
52211     /**
52212      * @cfg {String/Object} autoCreate
52213      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52214      */
52215
52216     // private
52217     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52218
52219     // private
52220     onRender : function(ct, position){
52221         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52222         if(this.legend){
52223             this.setLegend(this.legend);
52224         }
52225     },
52226
52227     // private
52228     setLegend : function(text){
52229         if(this.rendered){
52230             this.el.child('legend').update(text);
52231         }
52232     }
52233 });/*
52234  * Based on:
52235  * Ext JS Library 1.1.1
52236  * Copyright(c) 2006-2007, Ext JS, LLC.
52237  *
52238  * Originally Released Under LGPL - original licence link has changed is not relivant.
52239  *
52240  * Fork - LGPL
52241  * <script type="text/javascript">
52242  */
52243 /**
52244  * @class Roo.form.VTypes
52245  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52246  * @static
52247  */
52248 Roo.form.VTypes = function(){
52249     // closure these in so they are only created once.
52250     var alpha = /^[a-zA-Z_]+$/;
52251     var alphanum = /^[a-zA-Z0-9_]+$/;
52252     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52253     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52254
52255     // All these messages and functions are configurable
52256     return {
52257         /**
52258          * The function used to validate email addresses
52259          * @param {String} value The email address
52260          */
52261         'email' : function(v){
52262             return email.test(v);
52263         },
52264         /**
52265          * The error text to display when the email validation function returns false
52266          * @type String
52267          */
52268         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52269         /**
52270          * The keystroke filter mask to be applied on email input
52271          * @type RegExp
52272          */
52273         'emailMask' : /[a-z0-9_\.\-@]/i,
52274
52275         /**
52276          * The function used to validate URLs
52277          * @param {String} value The URL
52278          */
52279         'url' : function(v){
52280             return url.test(v);
52281         },
52282         /**
52283          * The error text to display when the url validation function returns false
52284          * @type String
52285          */
52286         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52287         
52288         /**
52289          * The function used to validate alpha values
52290          * @param {String} value The value
52291          */
52292         'alpha' : function(v){
52293             return alpha.test(v);
52294         },
52295         /**
52296          * The error text to display when the alpha validation function returns false
52297          * @type String
52298          */
52299         'alphaText' : 'This field should only contain letters and _',
52300         /**
52301          * The keystroke filter mask to be applied on alpha input
52302          * @type RegExp
52303          */
52304         'alphaMask' : /[a-z_]/i,
52305
52306         /**
52307          * The function used to validate alphanumeric values
52308          * @param {String} value The value
52309          */
52310         'alphanum' : function(v){
52311             return alphanum.test(v);
52312         },
52313         /**
52314          * The error text to display when the alphanumeric validation function returns false
52315          * @type String
52316          */
52317         'alphanumText' : 'This field should only contain letters, numbers and _',
52318         /**
52319          * The keystroke filter mask to be applied on alphanumeric input
52320          * @type RegExp
52321          */
52322         'alphanumMask' : /[a-z0-9_]/i
52323     };
52324 }();//<script type="text/javascript">
52325
52326 /**
52327  * @class Roo.form.FCKeditor
52328  * @extends Roo.form.TextArea
52329  * Wrapper around the FCKEditor http://www.fckeditor.net
52330  * @constructor
52331  * Creates a new FCKeditor
52332  * @param {Object} config Configuration options
52333  */
52334 Roo.form.FCKeditor = function(config){
52335     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52336     this.addEvents({
52337          /**
52338          * @event editorinit
52339          * Fired when the editor is initialized - you can add extra handlers here..
52340          * @param {FCKeditor} this
52341          * @param {Object} the FCK object.
52342          */
52343         editorinit : true
52344     });
52345     
52346     
52347 };
52348 Roo.form.FCKeditor.editors = { };
52349 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52350 {
52351     //defaultAutoCreate : {
52352     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52353     //},
52354     // private
52355     /**
52356      * @cfg {Object} fck options - see fck manual for details.
52357      */
52358     fckconfig : false,
52359     
52360     /**
52361      * @cfg {Object} fck toolbar set (Basic or Default)
52362      */
52363     toolbarSet : 'Basic',
52364     /**
52365      * @cfg {Object} fck BasePath
52366      */ 
52367     basePath : '/fckeditor/',
52368     
52369     
52370     frame : false,
52371     
52372     value : '',
52373     
52374    
52375     onRender : function(ct, position)
52376     {
52377         if(!this.el){
52378             this.defaultAutoCreate = {
52379                 tag: "textarea",
52380                 style:"width:300px;height:60px;",
52381                 autocomplete: "new-password"
52382             };
52383         }
52384         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52385         /*
52386         if(this.grow){
52387             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52388             if(this.preventScrollbars){
52389                 this.el.setStyle("overflow", "hidden");
52390             }
52391             this.el.setHeight(this.growMin);
52392         }
52393         */
52394         //console.log('onrender' + this.getId() );
52395         Roo.form.FCKeditor.editors[this.getId()] = this;
52396          
52397
52398         this.replaceTextarea() ;
52399         
52400     },
52401     
52402     getEditor : function() {
52403         return this.fckEditor;
52404     },
52405     /**
52406      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52407      * @param {Mixed} value The value to set
52408      */
52409     
52410     
52411     setValue : function(value)
52412     {
52413         //console.log('setValue: ' + value);
52414         
52415         if(typeof(value) == 'undefined') { // not sure why this is happending...
52416             return;
52417         }
52418         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52419         
52420         //if(!this.el || !this.getEditor()) {
52421         //    this.value = value;
52422             //this.setValue.defer(100,this,[value]);    
52423         //    return;
52424         //} 
52425         
52426         if(!this.getEditor()) {
52427             return;
52428         }
52429         
52430         this.getEditor().SetData(value);
52431         
52432         //
52433
52434     },
52435
52436     /**
52437      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52438      * @return {Mixed} value The field value
52439      */
52440     getValue : function()
52441     {
52442         
52443         if (this.frame && this.frame.dom.style.display == 'none') {
52444             return Roo.form.FCKeditor.superclass.getValue.call(this);
52445         }
52446         
52447         if(!this.el || !this.getEditor()) {
52448            
52449            // this.getValue.defer(100,this); 
52450             return this.value;
52451         }
52452        
52453         
52454         var value=this.getEditor().GetData();
52455         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52456         return Roo.form.FCKeditor.superclass.getValue.call(this);
52457         
52458
52459     },
52460
52461     /**
52462      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52463      * @return {Mixed} value The field value
52464      */
52465     getRawValue : function()
52466     {
52467         if (this.frame && this.frame.dom.style.display == 'none') {
52468             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52469         }
52470         
52471         if(!this.el || !this.getEditor()) {
52472             //this.getRawValue.defer(100,this); 
52473             return this.value;
52474             return;
52475         }
52476         
52477         
52478         
52479         var value=this.getEditor().GetData();
52480         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52481         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52482          
52483     },
52484     
52485     setSize : function(w,h) {
52486         
52487         
52488         
52489         //if (this.frame && this.frame.dom.style.display == 'none') {
52490         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52491         //    return;
52492         //}
52493         //if(!this.el || !this.getEditor()) {
52494         //    this.setSize.defer(100,this, [w,h]); 
52495         //    return;
52496         //}
52497         
52498         
52499         
52500         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52501         
52502         this.frame.dom.setAttribute('width', w);
52503         this.frame.dom.setAttribute('height', h);
52504         this.frame.setSize(w,h);
52505         
52506     },
52507     
52508     toggleSourceEdit : function(value) {
52509         
52510       
52511          
52512         this.el.dom.style.display = value ? '' : 'none';
52513         this.frame.dom.style.display = value ?  'none' : '';
52514         
52515     },
52516     
52517     
52518     focus: function(tag)
52519     {
52520         if (this.frame.dom.style.display == 'none') {
52521             return Roo.form.FCKeditor.superclass.focus.call(this);
52522         }
52523         if(!this.el || !this.getEditor()) {
52524             this.focus.defer(100,this, [tag]); 
52525             return;
52526         }
52527         
52528         
52529         
52530         
52531         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52532         this.getEditor().Focus();
52533         if (tgs.length) {
52534             if (!this.getEditor().Selection.GetSelection()) {
52535                 this.focus.defer(100,this, [tag]); 
52536                 return;
52537             }
52538             
52539             
52540             var r = this.getEditor().EditorDocument.createRange();
52541             r.setStart(tgs[0],0);
52542             r.setEnd(tgs[0],0);
52543             this.getEditor().Selection.GetSelection().removeAllRanges();
52544             this.getEditor().Selection.GetSelection().addRange(r);
52545             this.getEditor().Focus();
52546         }
52547         
52548     },
52549     
52550     
52551     
52552     replaceTextarea : function()
52553     {
52554         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52555             return ;
52556         }
52557         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52558         //{
52559             // We must check the elements firstly using the Id and then the name.
52560         var oTextarea = document.getElementById( this.getId() );
52561         
52562         var colElementsByName = document.getElementsByName( this.getId() ) ;
52563          
52564         oTextarea.style.display = 'none' ;
52565
52566         if ( oTextarea.tabIndex ) {            
52567             this.TabIndex = oTextarea.tabIndex ;
52568         }
52569         
52570         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52571         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52572         this.frame = Roo.get(this.getId() + '___Frame')
52573     },
52574     
52575     _getConfigHtml : function()
52576     {
52577         var sConfig = '' ;
52578
52579         for ( var o in this.fckconfig ) {
52580             sConfig += sConfig.length > 0  ? '&amp;' : '';
52581             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52582         }
52583
52584         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52585     },
52586     
52587     
52588     _getIFrameHtml : function()
52589     {
52590         var sFile = 'fckeditor.html' ;
52591         /* no idea what this is about..
52592         try
52593         {
52594             if ( (/fcksource=true/i).test( window.top.location.search ) )
52595                 sFile = 'fckeditor.original.html' ;
52596         }
52597         catch (e) { 
52598         */
52599
52600         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52601         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52602         
52603         
52604         var html = '<iframe id="' + this.getId() +
52605             '___Frame" src="' + sLink +
52606             '" width="' + this.width +
52607             '" height="' + this.height + '"' +
52608             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52609             ' frameborder="0" scrolling="no"></iframe>' ;
52610
52611         return html ;
52612     },
52613     
52614     _insertHtmlBefore : function( html, element )
52615     {
52616         if ( element.insertAdjacentHTML )       {
52617             // IE
52618             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52619         } else { // Gecko
52620             var oRange = document.createRange() ;
52621             oRange.setStartBefore( element ) ;
52622             var oFragment = oRange.createContextualFragment( html );
52623             element.parentNode.insertBefore( oFragment, element ) ;
52624         }
52625     }
52626     
52627     
52628   
52629     
52630     
52631     
52632     
52633
52634 });
52635
52636 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52637
52638 function FCKeditor_OnComplete(editorInstance){
52639     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52640     f.fckEditor = editorInstance;
52641     //console.log("loaded");
52642     f.fireEvent('editorinit', f, editorInstance);
52643
52644   
52645
52646  
52647
52648
52649
52650
52651
52652
52653
52654
52655
52656
52657
52658
52659
52660
52661
52662 //<script type="text/javascript">
52663 /**
52664  * @class Roo.form.GridField
52665  * @extends Roo.form.Field
52666  * Embed a grid (or editable grid into a form)
52667  * STATUS ALPHA
52668  * 
52669  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52670  * it needs 
52671  * xgrid.store = Roo.data.Store
52672  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52673  * xgrid.store.reader = Roo.data.JsonReader 
52674  * 
52675  * 
52676  * @constructor
52677  * Creates a new GridField
52678  * @param {Object} config Configuration options
52679  */
52680 Roo.form.GridField = function(config){
52681     Roo.form.GridField.superclass.constructor.call(this, config);
52682      
52683 };
52684
52685 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52686     /**
52687      * @cfg {Number} width  - used to restrict width of grid..
52688      */
52689     width : 100,
52690     /**
52691      * @cfg {Number} height - used to restrict height of grid..
52692      */
52693     height : 50,
52694      /**
52695      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52696          * 
52697          *}
52698      */
52699     xgrid : false, 
52700     /**
52701      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52702      * {tag: "input", type: "checkbox", autocomplete: "off"})
52703      */
52704    // defaultAutoCreate : { tag: 'div' },
52705     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52706     /**
52707      * @cfg {String} addTitle Text to include for adding a title.
52708      */
52709     addTitle : false,
52710     //
52711     onResize : function(){
52712         Roo.form.Field.superclass.onResize.apply(this, arguments);
52713     },
52714
52715     initEvents : function(){
52716         // Roo.form.Checkbox.superclass.initEvents.call(this);
52717         // has no events...
52718        
52719     },
52720
52721
52722     getResizeEl : function(){
52723         return this.wrap;
52724     },
52725
52726     getPositionEl : function(){
52727         return this.wrap;
52728     },
52729
52730     // private
52731     onRender : function(ct, position){
52732         
52733         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52734         var style = this.style;
52735         delete this.style;
52736         
52737         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52738         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52739         this.viewEl = this.wrap.createChild({ tag: 'div' });
52740         if (style) {
52741             this.viewEl.applyStyles(style);
52742         }
52743         if (this.width) {
52744             this.viewEl.setWidth(this.width);
52745         }
52746         if (this.height) {
52747             this.viewEl.setHeight(this.height);
52748         }
52749         //if(this.inputValue !== undefined){
52750         //this.setValue(this.value);
52751         
52752         
52753         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52754         
52755         
52756         this.grid.render();
52757         this.grid.getDataSource().on('remove', this.refreshValue, this);
52758         this.grid.getDataSource().on('update', this.refreshValue, this);
52759         this.grid.on('afteredit', this.refreshValue, this);
52760  
52761     },
52762      
52763     
52764     /**
52765      * Sets the value of the item. 
52766      * @param {String} either an object  or a string..
52767      */
52768     setValue : function(v){
52769         //this.value = v;
52770         v = v || []; // empty set..
52771         // this does not seem smart - it really only affects memoryproxy grids..
52772         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52773             var ds = this.grid.getDataSource();
52774             // assumes a json reader..
52775             var data = {}
52776             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52777             ds.loadData( data);
52778         }
52779         // clear selection so it does not get stale.
52780         if (this.grid.sm) { 
52781             this.grid.sm.clearSelections();
52782         }
52783         
52784         Roo.form.GridField.superclass.setValue.call(this, v);
52785         this.refreshValue();
52786         // should load data in the grid really....
52787     },
52788     
52789     // private
52790     refreshValue: function() {
52791          var val = [];
52792         this.grid.getDataSource().each(function(r) {
52793             val.push(r.data);
52794         });
52795         this.el.dom.value = Roo.encode(val);
52796     }
52797     
52798      
52799     
52800     
52801 });/*
52802  * Based on:
52803  * Ext JS Library 1.1.1
52804  * Copyright(c) 2006-2007, Ext JS, LLC.
52805  *
52806  * Originally Released Under LGPL - original licence link has changed is not relivant.
52807  *
52808  * Fork - LGPL
52809  * <script type="text/javascript">
52810  */
52811 /**
52812  * @class Roo.form.DisplayField
52813  * @extends Roo.form.Field
52814  * A generic Field to display non-editable data.
52815  * @cfg {Boolean} closable (true|false) default false
52816  * @constructor
52817  * Creates a new Display Field item.
52818  * @param {Object} config Configuration options
52819  */
52820 Roo.form.DisplayField = function(config){
52821     Roo.form.DisplayField.superclass.constructor.call(this, config);
52822     
52823     this.addEvents({
52824         /**
52825          * @event close
52826          * Fires after the click the close btn
52827              * @param {Roo.form.DisplayField} this
52828              */
52829         close : true
52830     });
52831 };
52832
52833 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52834     inputType:      'hidden',
52835     allowBlank:     true,
52836     readOnly:         true,
52837     
52838  
52839     /**
52840      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52841      */
52842     focusClass : undefined,
52843     /**
52844      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52845      */
52846     fieldClass: 'x-form-field',
52847     
52848      /**
52849      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52850      */
52851     valueRenderer: undefined,
52852     
52853     width: 100,
52854     /**
52855      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52856      * {tag: "input", type: "checkbox", autocomplete: "off"})
52857      */
52858      
52859  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52860  
52861     closable : false,
52862     
52863     onResize : function(){
52864         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52865         
52866     },
52867
52868     initEvents : function(){
52869         // Roo.form.Checkbox.superclass.initEvents.call(this);
52870         // has no events...
52871         
52872         if(this.closable){
52873             this.closeEl.on('click', this.onClose, this);
52874         }
52875        
52876     },
52877
52878
52879     getResizeEl : function(){
52880         return this.wrap;
52881     },
52882
52883     getPositionEl : function(){
52884         return this.wrap;
52885     },
52886
52887     // private
52888     onRender : function(ct, position){
52889         
52890         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52891         //if(this.inputValue !== undefined){
52892         this.wrap = this.el.wrap();
52893         
52894         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52895         
52896         if(this.closable){
52897             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52898         }
52899         
52900         if (this.bodyStyle) {
52901             this.viewEl.applyStyles(this.bodyStyle);
52902         }
52903         //this.viewEl.setStyle('padding', '2px');
52904         
52905         this.setValue(this.value);
52906         
52907     },
52908 /*
52909     // private
52910     initValue : Roo.emptyFn,
52911
52912   */
52913
52914         // private
52915     onClick : function(){
52916         
52917     },
52918
52919     /**
52920      * Sets the checked state of the checkbox.
52921      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52922      */
52923     setValue : function(v){
52924         this.value = v;
52925         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52926         // this might be called before we have a dom element..
52927         if (!this.viewEl) {
52928             return;
52929         }
52930         this.viewEl.dom.innerHTML = html;
52931         Roo.form.DisplayField.superclass.setValue.call(this, v);
52932
52933     },
52934     
52935     onClose : function(e)
52936     {
52937         e.preventDefault();
52938         
52939         this.fireEvent('close', this);
52940     }
52941 });/*
52942  * 
52943  * Licence- LGPL
52944  * 
52945  */
52946
52947 /**
52948  * @class Roo.form.DayPicker
52949  * @extends Roo.form.Field
52950  * A Day picker show [M] [T] [W] ....
52951  * @constructor
52952  * Creates a new Day Picker
52953  * @param {Object} config Configuration options
52954  */
52955 Roo.form.DayPicker= function(config){
52956     Roo.form.DayPicker.superclass.constructor.call(this, config);
52957      
52958 };
52959
52960 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52961     /**
52962      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52963      */
52964     focusClass : undefined,
52965     /**
52966      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52967      */
52968     fieldClass: "x-form-field",
52969    
52970     /**
52971      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52972      * {tag: "input", type: "checkbox", autocomplete: "off"})
52973      */
52974     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52975     
52976    
52977     actionMode : 'viewEl', 
52978     //
52979     // private
52980  
52981     inputType : 'hidden',
52982     
52983      
52984     inputElement: false, // real input element?
52985     basedOn: false, // ????
52986     
52987     isFormField: true, // not sure where this is needed!!!!
52988
52989     onResize : function(){
52990         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52991         if(!this.boxLabel){
52992             this.el.alignTo(this.wrap, 'c-c');
52993         }
52994     },
52995
52996     initEvents : function(){
52997         Roo.form.Checkbox.superclass.initEvents.call(this);
52998         this.el.on("click", this.onClick,  this);
52999         this.el.on("change", this.onClick,  this);
53000     },
53001
53002
53003     getResizeEl : function(){
53004         return this.wrap;
53005     },
53006
53007     getPositionEl : function(){
53008         return this.wrap;
53009     },
53010
53011     
53012     // private
53013     onRender : function(ct, position){
53014         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53015        
53016         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53017         
53018         var r1 = '<table><tr>';
53019         var r2 = '<tr class="x-form-daypick-icons">';
53020         for (var i=0; i < 7; i++) {
53021             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53022             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53023         }
53024         
53025         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53026         viewEl.select('img').on('click', this.onClick, this);
53027         this.viewEl = viewEl;   
53028         
53029         
53030         // this will not work on Chrome!!!
53031         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53032         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53033         
53034         
53035           
53036
53037     },
53038
53039     // private
53040     initValue : Roo.emptyFn,
53041
53042     /**
53043      * Returns the checked state of the checkbox.
53044      * @return {Boolean} True if checked, else false
53045      */
53046     getValue : function(){
53047         return this.el.dom.value;
53048         
53049     },
53050
53051         // private
53052     onClick : function(e){ 
53053         //this.setChecked(!this.checked);
53054         Roo.get(e.target).toggleClass('x-menu-item-checked');
53055         this.refreshValue();
53056         //if(this.el.dom.checked != this.checked){
53057         //    this.setValue(this.el.dom.checked);
53058        // }
53059     },
53060     
53061     // private
53062     refreshValue : function()
53063     {
53064         var val = '';
53065         this.viewEl.select('img',true).each(function(e,i,n)  {
53066             val += e.is(".x-menu-item-checked") ? String(n) : '';
53067         });
53068         this.setValue(val, true);
53069     },
53070
53071     /**
53072      * Sets the checked state of the checkbox.
53073      * On is always based on a string comparison between inputValue and the param.
53074      * @param {Boolean/String} value - the value to set 
53075      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53076      */
53077     setValue : function(v,suppressEvent){
53078         if (!this.el.dom) {
53079             return;
53080         }
53081         var old = this.el.dom.value ;
53082         this.el.dom.value = v;
53083         if (suppressEvent) {
53084             return ;
53085         }
53086          
53087         // update display..
53088         this.viewEl.select('img',true).each(function(e,i,n)  {
53089             
53090             var on = e.is(".x-menu-item-checked");
53091             var newv = v.indexOf(String(n)) > -1;
53092             if (on != newv) {
53093                 e.toggleClass('x-menu-item-checked');
53094             }
53095             
53096         });
53097         
53098         
53099         this.fireEvent('change', this, v, old);
53100         
53101         
53102     },
53103    
53104     // handle setting of hidden value by some other method!!?!?
53105     setFromHidden: function()
53106     {
53107         if(!this.el){
53108             return;
53109         }
53110         //console.log("SET FROM HIDDEN");
53111         //alert('setFrom hidden');
53112         this.setValue(this.el.dom.value);
53113     },
53114     
53115     onDestroy : function()
53116     {
53117         if(this.viewEl){
53118             Roo.get(this.viewEl).remove();
53119         }
53120          
53121         Roo.form.DayPicker.superclass.onDestroy.call(this);
53122     }
53123
53124 });/*
53125  * RooJS Library 1.1.1
53126  * Copyright(c) 2008-2011  Alan Knowles
53127  *
53128  * License - LGPL
53129  */
53130  
53131
53132 /**
53133  * @class Roo.form.ComboCheck
53134  * @extends Roo.form.ComboBox
53135  * A combobox for multiple select items.
53136  *
53137  * FIXME - could do with a reset button..
53138  * 
53139  * @constructor
53140  * Create a new ComboCheck
53141  * @param {Object} config Configuration options
53142  */
53143 Roo.form.ComboCheck = function(config){
53144     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53145     // should verify some data...
53146     // like
53147     // hiddenName = required..
53148     // displayField = required
53149     // valudField == required
53150     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53151     var _t = this;
53152     Roo.each(req, function(e) {
53153         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53154             throw "Roo.form.ComboCheck : missing value for: " + e;
53155         }
53156     });
53157     
53158     
53159 };
53160
53161 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53162      
53163      
53164     editable : false,
53165      
53166     selectedClass: 'x-menu-item-checked', 
53167     
53168     // private
53169     onRender : function(ct, position){
53170         var _t = this;
53171         
53172         
53173         
53174         if(!this.tpl){
53175             var cls = 'x-combo-list';
53176
53177             
53178             this.tpl =  new Roo.Template({
53179                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53180                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53181                    '<span>{' + this.displayField + '}</span>' +
53182                     '</div>' 
53183                 
53184             });
53185         }
53186  
53187         
53188         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53189         this.view.singleSelect = false;
53190         this.view.multiSelect = true;
53191         this.view.toggleSelect = true;
53192         this.pageTb.add(new Roo.Toolbar.Fill(), {
53193             
53194             text: 'Done',
53195             handler: function()
53196             {
53197                 _t.collapse();
53198             }
53199         });
53200     },
53201     
53202     onViewOver : function(e, t){
53203         // do nothing...
53204         return;
53205         
53206     },
53207     
53208     onViewClick : function(doFocus,index){
53209         return;
53210         
53211     },
53212     select: function () {
53213         //Roo.log("SELECT CALLED");
53214     },
53215      
53216     selectByValue : function(xv, scrollIntoView){
53217         var ar = this.getValueArray();
53218         var sels = [];
53219         
53220         Roo.each(ar, function(v) {
53221             if(v === undefined || v === null){
53222                 return;
53223             }
53224             var r = this.findRecord(this.valueField, v);
53225             if(r){
53226                 sels.push(this.store.indexOf(r))
53227                 
53228             }
53229         },this);
53230         this.view.select(sels);
53231         return false;
53232     },
53233     
53234     
53235     
53236     onSelect : function(record, index){
53237        // Roo.log("onselect Called");
53238        // this is only called by the clear button now..
53239         this.view.clearSelections();
53240         this.setValue('[]');
53241         if (this.value != this.valueBefore) {
53242             this.fireEvent('change', this, this.value, this.valueBefore);
53243             this.valueBefore = this.value;
53244         }
53245     },
53246     getValueArray : function()
53247     {
53248         var ar = [] ;
53249         
53250         try {
53251             //Roo.log(this.value);
53252             if (typeof(this.value) == 'undefined') {
53253                 return [];
53254             }
53255             var ar = Roo.decode(this.value);
53256             return  ar instanceof Array ? ar : []; //?? valid?
53257             
53258         } catch(e) {
53259             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53260             return [];
53261         }
53262          
53263     },
53264     expand : function ()
53265     {
53266         
53267         Roo.form.ComboCheck.superclass.expand.call(this);
53268         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53269         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53270         
53271
53272     },
53273     
53274     collapse : function(){
53275         Roo.form.ComboCheck.superclass.collapse.call(this);
53276         var sl = this.view.getSelectedIndexes();
53277         var st = this.store;
53278         var nv = [];
53279         var tv = [];
53280         var r;
53281         Roo.each(sl, function(i) {
53282             r = st.getAt(i);
53283             nv.push(r.get(this.valueField));
53284         },this);
53285         this.setValue(Roo.encode(nv));
53286         if (this.value != this.valueBefore) {
53287
53288             this.fireEvent('change', this, this.value, this.valueBefore);
53289             this.valueBefore = this.value;
53290         }
53291         
53292     },
53293     
53294     setValue : function(v){
53295         // Roo.log(v);
53296         this.value = v;
53297         
53298         var vals = this.getValueArray();
53299         var tv = [];
53300         Roo.each(vals, function(k) {
53301             var r = this.findRecord(this.valueField, k);
53302             if(r){
53303                 tv.push(r.data[this.displayField]);
53304             }else if(this.valueNotFoundText !== undefined){
53305                 tv.push( this.valueNotFoundText );
53306             }
53307         },this);
53308        // Roo.log(tv);
53309         
53310         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53311         this.hiddenField.value = v;
53312         this.value = v;
53313     }
53314     
53315 });/*
53316  * Based on:
53317  * Ext JS Library 1.1.1
53318  * Copyright(c) 2006-2007, Ext JS, LLC.
53319  *
53320  * Originally Released Under LGPL - original licence link has changed is not relivant.
53321  *
53322  * Fork - LGPL
53323  * <script type="text/javascript">
53324  */
53325  
53326 /**
53327  * @class Roo.form.Signature
53328  * @extends Roo.form.Field
53329  * Signature field.  
53330  * @constructor
53331  * 
53332  * @param {Object} config Configuration options
53333  */
53334
53335 Roo.form.Signature = function(config){
53336     Roo.form.Signature.superclass.constructor.call(this, config);
53337     
53338     this.addEvents({// not in used??
53339          /**
53340          * @event confirm
53341          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53342              * @param {Roo.form.Signature} combo This combo box
53343              */
53344         'confirm' : true,
53345         /**
53346          * @event reset
53347          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53348              * @param {Roo.form.ComboBox} combo This combo box
53349              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53350              */
53351         'reset' : true
53352     });
53353 };
53354
53355 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53356     /**
53357      * @cfg {Object} labels Label to use when rendering a form.
53358      * defaults to 
53359      * labels : { 
53360      *      clear : "Clear",
53361      *      confirm : "Confirm"
53362      *  }
53363      */
53364     labels : { 
53365         clear : "Clear",
53366         confirm : "Confirm"
53367     },
53368     /**
53369      * @cfg {Number} width The signature panel width (defaults to 300)
53370      */
53371     width: 300,
53372     /**
53373      * @cfg {Number} height The signature panel height (defaults to 100)
53374      */
53375     height : 100,
53376     /**
53377      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53378      */
53379     allowBlank : false,
53380     
53381     //private
53382     // {Object} signPanel The signature SVG panel element (defaults to {})
53383     signPanel : {},
53384     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53385     isMouseDown : false,
53386     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53387     isConfirmed : false,
53388     // {String} signatureTmp SVG mapping string (defaults to empty string)
53389     signatureTmp : '',
53390     
53391     
53392     defaultAutoCreate : { // modified by initCompnoent..
53393         tag: "input",
53394         type:"hidden"
53395     },
53396
53397     // private
53398     onRender : function(ct, position){
53399         
53400         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53401         
53402         this.wrap = this.el.wrap({
53403             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53404         });
53405         
53406         this.createToolbar(this);
53407         this.signPanel = this.wrap.createChild({
53408                 tag: 'div',
53409                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53410             }, this.el
53411         );
53412             
53413         this.svgID = Roo.id();
53414         this.svgEl = this.signPanel.createChild({
53415               xmlns : 'http://www.w3.org/2000/svg',
53416               tag : 'svg',
53417               id : this.svgID + "-svg",
53418               width: this.width,
53419               height: this.height,
53420               viewBox: '0 0 '+this.width+' '+this.height,
53421               cn : [
53422                 {
53423                     tag: "rect",
53424                     id: this.svgID + "-svg-r",
53425                     width: this.width,
53426                     height: this.height,
53427                     fill: "#ffa"
53428                 },
53429                 {
53430                     tag: "line",
53431                     id: this.svgID + "-svg-l",
53432                     x1: "0", // start
53433                     y1: (this.height*0.8), // start set the line in 80% of height
53434                     x2: this.width, // end
53435                     y2: (this.height*0.8), // end set the line in 80% of height
53436                     'stroke': "#666",
53437                     'stroke-width': "1",
53438                     'stroke-dasharray': "3",
53439                     'shape-rendering': "crispEdges",
53440                     'pointer-events': "none"
53441                 },
53442                 {
53443                     tag: "path",
53444                     id: this.svgID + "-svg-p",
53445                     'stroke': "navy",
53446                     'stroke-width': "3",
53447                     'fill': "none",
53448                     'pointer-events': 'none'
53449                 }
53450               ]
53451         });
53452         this.createSVG();
53453         this.svgBox = this.svgEl.dom.getScreenCTM();
53454     },
53455     createSVG : function(){ 
53456         var svg = this.signPanel;
53457         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53458         var t = this;
53459
53460         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53461         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53462         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53463         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53464         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53465         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53466         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53467         
53468     },
53469     isTouchEvent : function(e){
53470         return e.type.match(/^touch/);
53471     },
53472     getCoords : function (e) {
53473         var pt    = this.svgEl.dom.createSVGPoint();
53474         pt.x = e.clientX; 
53475         pt.y = e.clientY;
53476         if (this.isTouchEvent(e)) {
53477             pt.x =  e.targetTouches[0].clientX;
53478             pt.y = e.targetTouches[0].clientY;
53479         }
53480         var a = this.svgEl.dom.getScreenCTM();
53481         var b = a.inverse();
53482         var mx = pt.matrixTransform(b);
53483         return mx.x + ',' + mx.y;
53484     },
53485     //mouse event headler 
53486     down : function (e) {
53487         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53488         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53489         
53490         this.isMouseDown = true;
53491         
53492         e.preventDefault();
53493     },
53494     move : function (e) {
53495         if (this.isMouseDown) {
53496             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53497             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53498         }
53499         
53500         e.preventDefault();
53501     },
53502     up : function (e) {
53503         this.isMouseDown = false;
53504         var sp = this.signatureTmp.split(' ');
53505         
53506         if(sp.length > 1){
53507             if(!sp[sp.length-2].match(/^L/)){
53508                 sp.pop();
53509                 sp.pop();
53510                 sp.push("");
53511                 this.signatureTmp = sp.join(" ");
53512             }
53513         }
53514         if(this.getValue() != this.signatureTmp){
53515             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53516             this.isConfirmed = false;
53517         }
53518         e.preventDefault();
53519     },
53520     
53521     /**
53522      * Protected method that will not generally be called directly. It
53523      * is called when the editor creates its toolbar. Override this method if you need to
53524      * add custom toolbar buttons.
53525      * @param {HtmlEditor} editor
53526      */
53527     createToolbar : function(editor){
53528          function btn(id, toggle, handler){
53529             var xid = fid + '-'+ id ;
53530             return {
53531                 id : xid,
53532                 cmd : id,
53533                 cls : 'x-btn-icon x-edit-'+id,
53534                 enableToggle:toggle !== false,
53535                 scope: editor, // was editor...
53536                 handler:handler||editor.relayBtnCmd,
53537                 clickEvent:'mousedown',
53538                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53539                 tabIndex:-1
53540             };
53541         }
53542         
53543         
53544         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53545         this.tb = tb;
53546         this.tb.add(
53547            {
53548                 cls : ' x-signature-btn x-signature-'+id,
53549                 scope: editor, // was editor...
53550                 handler: this.reset,
53551                 clickEvent:'mousedown',
53552                 text: this.labels.clear
53553             },
53554             {
53555                  xtype : 'Fill',
53556                  xns: Roo.Toolbar
53557             }, 
53558             {
53559                 cls : '  x-signature-btn x-signature-'+id,
53560                 scope: editor, // was editor...
53561                 handler: this.confirmHandler,
53562                 clickEvent:'mousedown',
53563                 text: this.labels.confirm
53564             }
53565         );
53566     
53567     },
53568     //public
53569     /**
53570      * when user is clicked confirm then show this image.....
53571      * 
53572      * @return {String} Image Data URI
53573      */
53574     getImageDataURI : function(){
53575         var svg = this.svgEl.dom.parentNode.innerHTML;
53576         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53577         return src; 
53578     },
53579     /**
53580      * 
53581      * @return {Boolean} this.isConfirmed
53582      */
53583     getConfirmed : function(){
53584         return this.isConfirmed;
53585     },
53586     /**
53587      * 
53588      * @return {Number} this.width
53589      */
53590     getWidth : function(){
53591         return this.width;
53592     },
53593     /**
53594      * 
53595      * @return {Number} this.height
53596      */
53597     getHeight : function(){
53598         return this.height;
53599     },
53600     // private
53601     getSignature : function(){
53602         return this.signatureTmp;
53603     },
53604     // private
53605     reset : function(){
53606         this.signatureTmp = '';
53607         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53608         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53609         this.isConfirmed = false;
53610         Roo.form.Signature.superclass.reset.call(this);
53611     },
53612     setSignature : function(s){
53613         this.signatureTmp = s;
53614         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53615         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53616         this.setValue(s);
53617         this.isConfirmed = false;
53618         Roo.form.Signature.superclass.reset.call(this);
53619     }, 
53620     test : function(){
53621 //        Roo.log(this.signPanel.dom.contentWindow.up())
53622     },
53623     //private
53624     setConfirmed : function(){
53625         
53626         
53627         
53628 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53629     },
53630     // private
53631     confirmHandler : function(){
53632         if(!this.getSignature()){
53633             return;
53634         }
53635         
53636         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53637         this.setValue(this.getSignature());
53638         this.isConfirmed = true;
53639         
53640         this.fireEvent('confirm', this);
53641     },
53642     // private
53643     // Subclasses should provide the validation implementation by overriding this
53644     validateValue : function(value){
53645         if(this.allowBlank){
53646             return true;
53647         }
53648         
53649         if(this.isConfirmed){
53650             return true;
53651         }
53652         return false;
53653     }
53654 });/*
53655  * Based on:
53656  * Ext JS Library 1.1.1
53657  * Copyright(c) 2006-2007, Ext JS, LLC.
53658  *
53659  * Originally Released Under LGPL - original licence link has changed is not relivant.
53660  *
53661  * Fork - LGPL
53662  * <script type="text/javascript">
53663  */
53664  
53665
53666 /**
53667  * @class Roo.form.ComboBox
53668  * @extends Roo.form.TriggerField
53669  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53670  * @constructor
53671  * Create a new ComboBox.
53672  * @param {Object} config Configuration options
53673  */
53674 Roo.form.Select = function(config){
53675     Roo.form.Select.superclass.constructor.call(this, config);
53676      
53677 };
53678
53679 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53680     /**
53681      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53682      */
53683     /**
53684      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53685      * rendering into an Roo.Editor, defaults to false)
53686      */
53687     /**
53688      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53689      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53690      */
53691     /**
53692      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53693      */
53694     /**
53695      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53696      * the dropdown list (defaults to undefined, with no header element)
53697      */
53698
53699      /**
53700      * @cfg {String/Roo.Template} tpl The template to use to render the output
53701      */
53702      
53703     // private
53704     defaultAutoCreate : {tag: "select"  },
53705     /**
53706      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53707      */
53708     listWidth: undefined,
53709     /**
53710      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53711      * mode = 'remote' or 'text' if mode = 'local')
53712      */
53713     displayField: undefined,
53714     /**
53715      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53716      * mode = 'remote' or 'value' if mode = 'local'). 
53717      * Note: use of a valueField requires the user make a selection
53718      * in order for a value to be mapped.
53719      */
53720     valueField: undefined,
53721     
53722     
53723     /**
53724      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53725      * field's data value (defaults to the underlying DOM element's name)
53726      */
53727     hiddenName: undefined,
53728     /**
53729      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53730      */
53731     listClass: '',
53732     /**
53733      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53734      */
53735     selectedClass: 'x-combo-selected',
53736     /**
53737      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53738      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53739      * which displays a downward arrow icon).
53740      */
53741     triggerClass : 'x-form-arrow-trigger',
53742     /**
53743      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53744      */
53745     shadow:'sides',
53746     /**
53747      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53748      * anchor positions (defaults to 'tl-bl')
53749      */
53750     listAlign: 'tl-bl?',
53751     /**
53752      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53753      */
53754     maxHeight: 300,
53755     /**
53756      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53757      * query specified by the allQuery config option (defaults to 'query')
53758      */
53759     triggerAction: 'query',
53760     /**
53761      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53762      * (defaults to 4, does not apply if editable = false)
53763      */
53764     minChars : 4,
53765     /**
53766      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53767      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53768      */
53769     typeAhead: false,
53770     /**
53771      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53772      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53773      */
53774     queryDelay: 500,
53775     /**
53776      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53777      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53778      */
53779     pageSize: 0,
53780     /**
53781      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53782      * when editable = true (defaults to false)
53783      */
53784     selectOnFocus:false,
53785     /**
53786      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53787      */
53788     queryParam: 'query',
53789     /**
53790      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53791      * when mode = 'remote' (defaults to 'Loading...')
53792      */
53793     loadingText: 'Loading...',
53794     /**
53795      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53796      */
53797     resizable: false,
53798     /**
53799      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53800      */
53801     handleHeight : 8,
53802     /**
53803      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53804      * traditional select (defaults to true)
53805      */
53806     editable: true,
53807     /**
53808      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53809      */
53810     allQuery: '',
53811     /**
53812      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53813      */
53814     mode: 'remote',
53815     /**
53816      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53817      * listWidth has a higher value)
53818      */
53819     minListWidth : 70,
53820     /**
53821      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53822      * allow the user to set arbitrary text into the field (defaults to false)
53823      */
53824     forceSelection:false,
53825     /**
53826      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53827      * if typeAhead = true (defaults to 250)
53828      */
53829     typeAheadDelay : 250,
53830     /**
53831      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53832      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53833      */
53834     valueNotFoundText : undefined,
53835     
53836     /**
53837      * @cfg {String} defaultValue The value displayed after loading the store.
53838      */
53839     defaultValue: '',
53840     
53841     /**
53842      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53843      */
53844     blockFocus : false,
53845     
53846     /**
53847      * @cfg {Boolean} disableClear Disable showing of clear button.
53848      */
53849     disableClear : false,
53850     /**
53851      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53852      */
53853     alwaysQuery : false,
53854     
53855     //private
53856     addicon : false,
53857     editicon: false,
53858     
53859     // element that contains real text value.. (when hidden is used..)
53860      
53861     // private
53862     onRender : function(ct, position){
53863         Roo.form.Field.prototype.onRender.call(this, ct, position);
53864         
53865         if(this.store){
53866             this.store.on('beforeload', this.onBeforeLoad, this);
53867             this.store.on('load', this.onLoad, this);
53868             this.store.on('loadexception', this.onLoadException, this);
53869             this.store.load({});
53870         }
53871         
53872         
53873         
53874     },
53875
53876     // private
53877     initEvents : function(){
53878         //Roo.form.ComboBox.superclass.initEvents.call(this);
53879  
53880     },
53881
53882     onDestroy : function(){
53883        
53884         if(this.store){
53885             this.store.un('beforeload', this.onBeforeLoad, this);
53886             this.store.un('load', this.onLoad, this);
53887             this.store.un('loadexception', this.onLoadException, this);
53888         }
53889         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53890     },
53891
53892     // private
53893     fireKey : function(e){
53894         if(e.isNavKeyPress() && !this.list.isVisible()){
53895             this.fireEvent("specialkey", this, e);
53896         }
53897     },
53898
53899     // private
53900     onResize: function(w, h){
53901         
53902         return; 
53903     
53904         
53905     },
53906
53907     /**
53908      * Allow or prevent the user from directly editing the field text.  If false is passed,
53909      * the user will only be able to select from the items defined in the dropdown list.  This method
53910      * is the runtime equivalent of setting the 'editable' config option at config time.
53911      * @param {Boolean} value True to allow the user to directly edit the field text
53912      */
53913     setEditable : function(value){
53914          
53915     },
53916
53917     // private
53918     onBeforeLoad : function(){
53919         
53920         Roo.log("Select before load");
53921         return;
53922     
53923         this.innerList.update(this.loadingText ?
53924                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53925         //this.restrictHeight();
53926         this.selectedIndex = -1;
53927     },
53928
53929     // private
53930     onLoad : function(){
53931
53932     
53933         var dom = this.el.dom;
53934         dom.innerHTML = '';
53935          var od = dom.ownerDocument;
53936          
53937         if (this.emptyText) {
53938             var op = od.createElement('option');
53939             op.setAttribute('value', '');
53940             op.innerHTML = String.format('{0}', this.emptyText);
53941             dom.appendChild(op);
53942         }
53943         if(this.store.getCount() > 0){
53944            
53945             var vf = this.valueField;
53946             var df = this.displayField;
53947             this.store.data.each(function(r) {
53948                 // which colmsn to use... testing - cdoe / title..
53949                 var op = od.createElement('option');
53950                 op.setAttribute('value', r.data[vf]);
53951                 op.innerHTML = String.format('{0}', r.data[df]);
53952                 dom.appendChild(op);
53953             });
53954             if (typeof(this.defaultValue != 'undefined')) {
53955                 this.setValue(this.defaultValue);
53956             }
53957             
53958              
53959         }else{
53960             //this.onEmptyResults();
53961         }
53962         //this.el.focus();
53963     },
53964     // private
53965     onLoadException : function()
53966     {
53967         dom.innerHTML = '';
53968             
53969         Roo.log("Select on load exception");
53970         return;
53971     
53972         this.collapse();
53973         Roo.log(this.store.reader.jsonData);
53974         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53975             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53976         }
53977         
53978         
53979     },
53980     // private
53981     onTypeAhead : function(){
53982          
53983     },
53984
53985     // private
53986     onSelect : function(record, index){
53987         Roo.log('on select?');
53988         return;
53989         if(this.fireEvent('beforeselect', this, record, index) !== false){
53990             this.setFromData(index > -1 ? record.data : false);
53991             this.collapse();
53992             this.fireEvent('select', this, record, index);
53993         }
53994     },
53995
53996     /**
53997      * Returns the currently selected field value or empty string if no value is set.
53998      * @return {String} value The selected value
53999      */
54000     getValue : function(){
54001         var dom = this.el.dom;
54002         this.value = dom.options[dom.selectedIndex].value;
54003         return this.value;
54004         
54005     },
54006
54007     /**
54008      * Clears any text/value currently set in the field
54009      */
54010     clearValue : function(){
54011         this.value = '';
54012         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54013         
54014     },
54015
54016     /**
54017      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54018      * will be displayed in the field.  If the value does not match the data value of an existing item,
54019      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54020      * Otherwise the field will be blank (although the value will still be set).
54021      * @param {String} value The value to match
54022      */
54023     setValue : function(v){
54024         var d = this.el.dom;
54025         for (var i =0; i < d.options.length;i++) {
54026             if (v == d.options[i].value) {
54027                 d.selectedIndex = i;
54028                 this.value = v;
54029                 return;
54030             }
54031         }
54032         this.clearValue();
54033     },
54034     /**
54035      * @property {Object} the last set data for the element
54036      */
54037     
54038     lastData : false,
54039     /**
54040      * Sets the value of the field based on a object which is related to the record format for the store.
54041      * @param {Object} value the value to set as. or false on reset?
54042      */
54043     setFromData : function(o){
54044         Roo.log('setfrom data?');
54045          
54046         
54047         
54048     },
54049     // private
54050     reset : function(){
54051         this.clearValue();
54052     },
54053     // private
54054     findRecord : function(prop, value){
54055         
54056         return false;
54057     
54058         var record;
54059         if(this.store.getCount() > 0){
54060             this.store.each(function(r){
54061                 if(r.data[prop] == value){
54062                     record = r;
54063                     return false;
54064                 }
54065                 return true;
54066             });
54067         }
54068         return record;
54069     },
54070     
54071     getName: function()
54072     {
54073         // returns hidden if it's set..
54074         if (!this.rendered) {return ''};
54075         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54076         
54077     },
54078      
54079
54080     
54081
54082     // private
54083     onEmptyResults : function(){
54084         Roo.log('empty results');
54085         //this.collapse();
54086     },
54087
54088     /**
54089      * Returns true if the dropdown list is expanded, else false.
54090      */
54091     isExpanded : function(){
54092         return false;
54093     },
54094
54095     /**
54096      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54097      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54098      * @param {String} value The data value of the item to select
54099      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54100      * selected item if it is not currently in view (defaults to true)
54101      * @return {Boolean} True if the value matched an item in the list, else false
54102      */
54103     selectByValue : function(v, scrollIntoView){
54104         Roo.log('select By Value');
54105         return false;
54106     
54107         if(v !== undefined && v !== null){
54108             var r = this.findRecord(this.valueField || this.displayField, v);
54109             if(r){
54110                 this.select(this.store.indexOf(r), scrollIntoView);
54111                 return true;
54112             }
54113         }
54114         return false;
54115     },
54116
54117     /**
54118      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54119      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54120      * @param {Number} index The zero-based index of the list item to select
54121      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54122      * selected item if it is not currently in view (defaults to true)
54123      */
54124     select : function(index, scrollIntoView){
54125         Roo.log('select ');
54126         return  ;
54127         
54128         this.selectedIndex = index;
54129         this.view.select(index);
54130         if(scrollIntoView !== false){
54131             var el = this.view.getNode(index);
54132             if(el){
54133                 this.innerList.scrollChildIntoView(el, false);
54134             }
54135         }
54136     },
54137
54138       
54139
54140     // private
54141     validateBlur : function(){
54142         
54143         return;
54144         
54145     },
54146
54147     // private
54148     initQuery : function(){
54149         this.doQuery(this.getRawValue());
54150     },
54151
54152     // private
54153     doForce : function(){
54154         if(this.el.dom.value.length > 0){
54155             this.el.dom.value =
54156                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54157              
54158         }
54159     },
54160
54161     /**
54162      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54163      * query allowing the query action to be canceled if needed.
54164      * @param {String} query The SQL query to execute
54165      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54166      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54167      * saved in the current store (defaults to false)
54168      */
54169     doQuery : function(q, forceAll){
54170         
54171         Roo.log('doQuery?');
54172         if(q === undefined || q === null){
54173             q = '';
54174         }
54175         var qe = {
54176             query: q,
54177             forceAll: forceAll,
54178             combo: this,
54179             cancel:false
54180         };
54181         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54182             return false;
54183         }
54184         q = qe.query;
54185         forceAll = qe.forceAll;
54186         if(forceAll === true || (q.length >= this.minChars)){
54187             if(this.lastQuery != q || this.alwaysQuery){
54188                 this.lastQuery = q;
54189                 if(this.mode == 'local'){
54190                     this.selectedIndex = -1;
54191                     if(forceAll){
54192                         this.store.clearFilter();
54193                     }else{
54194                         this.store.filter(this.displayField, q);
54195                     }
54196                     this.onLoad();
54197                 }else{
54198                     this.store.baseParams[this.queryParam] = q;
54199                     this.store.load({
54200                         params: this.getParams(q)
54201                     });
54202                     this.expand();
54203                 }
54204             }else{
54205                 this.selectedIndex = -1;
54206                 this.onLoad();   
54207             }
54208         }
54209     },
54210
54211     // private
54212     getParams : function(q){
54213         var p = {};
54214         //p[this.queryParam] = q;
54215         if(this.pageSize){
54216             p.start = 0;
54217             p.limit = this.pageSize;
54218         }
54219         return p;
54220     },
54221
54222     /**
54223      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54224      */
54225     collapse : function(){
54226         
54227     },
54228
54229     // private
54230     collapseIf : function(e){
54231         
54232     },
54233
54234     /**
54235      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54236      */
54237     expand : function(){
54238         
54239     } ,
54240
54241     // private
54242      
54243
54244     /** 
54245     * @cfg {Boolean} grow 
54246     * @hide 
54247     */
54248     /** 
54249     * @cfg {Number} growMin 
54250     * @hide 
54251     */
54252     /** 
54253     * @cfg {Number} growMax 
54254     * @hide 
54255     */
54256     /**
54257      * @hide
54258      * @method autoSize
54259      */
54260     
54261     setWidth : function()
54262     {
54263         
54264     },
54265     getResizeEl : function(){
54266         return this.el;
54267     }
54268 });//<script type="text/javasscript">
54269  
54270
54271 /**
54272  * @class Roo.DDView
54273  * A DnD enabled version of Roo.View.
54274  * @param {Element/String} container The Element in which to create the View.
54275  * @param {String} tpl The template string used to create the markup for each element of the View
54276  * @param {Object} config The configuration properties. These include all the config options of
54277  * {@link Roo.View} plus some specific to this class.<br>
54278  * <p>
54279  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54280  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54281  * <p>
54282  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54283 .x-view-drag-insert-above {
54284         border-top:1px dotted #3366cc;
54285 }
54286 .x-view-drag-insert-below {
54287         border-bottom:1px dotted #3366cc;
54288 }
54289 </code></pre>
54290  * 
54291  */
54292  
54293 Roo.DDView = function(container, tpl, config) {
54294     Roo.DDView.superclass.constructor.apply(this, arguments);
54295     this.getEl().setStyle("outline", "0px none");
54296     this.getEl().unselectable();
54297     if (this.dragGroup) {
54298         this.setDraggable(this.dragGroup.split(","));
54299     }
54300     if (this.dropGroup) {
54301         this.setDroppable(this.dropGroup.split(","));
54302     }
54303     if (this.deletable) {
54304         this.setDeletable();
54305     }
54306     this.isDirtyFlag = false;
54307         this.addEvents({
54308                 "drop" : true
54309         });
54310 };
54311
54312 Roo.extend(Roo.DDView, Roo.View, {
54313 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54314 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54315 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54316 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54317
54318         isFormField: true,
54319
54320         reset: Roo.emptyFn,
54321         
54322         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54323
54324         validate: function() {
54325                 return true;
54326         },
54327         
54328         destroy: function() {
54329                 this.purgeListeners();
54330                 this.getEl.removeAllListeners();
54331                 this.getEl().remove();
54332                 if (this.dragZone) {
54333                         if (this.dragZone.destroy) {
54334                                 this.dragZone.destroy();
54335                         }
54336                 }
54337                 if (this.dropZone) {
54338                         if (this.dropZone.destroy) {
54339                                 this.dropZone.destroy();
54340                         }
54341                 }
54342         },
54343
54344 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54345         getName: function() {
54346                 return this.name;
54347         },
54348
54349 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54350         setValue: function(v) {
54351                 if (!this.store) {
54352                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54353                 }
54354                 var data = {};
54355                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54356                 this.store.proxy = new Roo.data.MemoryProxy(data);
54357                 this.store.load();
54358         },
54359
54360 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54361         getValue: function() {
54362                 var result = '(';
54363                 this.store.each(function(rec) {
54364                         result += rec.id + ',';
54365                 });
54366                 return result.substr(0, result.length - 1) + ')';
54367         },
54368         
54369         getIds: function() {
54370                 var i = 0, result = new Array(this.store.getCount());
54371                 this.store.each(function(rec) {
54372                         result[i++] = rec.id;
54373                 });
54374                 return result;
54375         },
54376         
54377         isDirty: function() {
54378                 return this.isDirtyFlag;
54379         },
54380
54381 /**
54382  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54383  *      whole Element becomes the target, and this causes the drop gesture to append.
54384  */
54385     getTargetFromEvent : function(e) {
54386                 var target = e.getTarget();
54387                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54388                 target = target.parentNode;
54389                 }
54390                 if (!target) {
54391                         target = this.el.dom.lastChild || this.el.dom;
54392                 }
54393                 return target;
54394     },
54395
54396 /**
54397  *      Create the drag data which consists of an object which has the property "ddel" as
54398  *      the drag proxy element. 
54399  */
54400     getDragData : function(e) {
54401         var target = this.findItemFromChild(e.getTarget());
54402                 if(target) {
54403                         this.handleSelection(e);
54404                         var selNodes = this.getSelectedNodes();
54405             var dragData = {
54406                 source: this,
54407                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54408                 nodes: selNodes,
54409                 records: []
54410                         };
54411                         var selectedIndices = this.getSelectedIndexes();
54412                         for (var i = 0; i < selectedIndices.length; i++) {
54413                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54414                         }
54415                         if (selNodes.length == 1) {
54416                                 dragData.ddel = target.cloneNode(true); // the div element
54417                         } else {
54418                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54419                                 div.className = 'multi-proxy';
54420                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54421                                         div.appendChild(selNodes[i].cloneNode(true));
54422                                 }
54423                                 dragData.ddel = div;
54424                         }
54425             //console.log(dragData)
54426             //console.log(dragData.ddel.innerHTML)
54427                         return dragData;
54428                 }
54429         //console.log('nodragData')
54430                 return false;
54431     },
54432     
54433 /**     Specify to which ddGroup items in this DDView may be dragged. */
54434     setDraggable: function(ddGroup) {
54435         if (ddGroup instanceof Array) {
54436                 Roo.each(ddGroup, this.setDraggable, this);
54437                 return;
54438         }
54439         if (this.dragZone) {
54440                 this.dragZone.addToGroup(ddGroup);
54441         } else {
54442                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54443                                 containerScroll: true,
54444                                 ddGroup: ddGroup 
54445
54446                         });
54447 //                      Draggability implies selection. DragZone's mousedown selects the element.
54448                         if (!this.multiSelect) { this.singleSelect = true; }
54449
54450 //                      Wire the DragZone's handlers up to methods in *this*
54451                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54452                 }
54453     },
54454
54455 /**     Specify from which ddGroup this DDView accepts drops. */
54456     setDroppable: function(ddGroup) {
54457         if (ddGroup instanceof Array) {
54458                 Roo.each(ddGroup, this.setDroppable, this);
54459                 return;
54460         }
54461         if (this.dropZone) {
54462                 this.dropZone.addToGroup(ddGroup);
54463         } else {
54464                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54465                                 containerScroll: true,
54466                                 ddGroup: ddGroup
54467                         });
54468
54469 //                      Wire the DropZone's handlers up to methods in *this*
54470                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54471                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54472                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54473                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54474                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54475                 }
54476     },
54477
54478 /**     Decide whether to drop above or below a View node. */
54479     getDropPoint : function(e, n, dd){
54480         if (n == this.el.dom) { return "above"; }
54481                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54482                 var c = t + (b - t) / 2;
54483                 var y = Roo.lib.Event.getPageY(e);
54484                 if(y <= c) {
54485                         return "above";
54486                 }else{
54487                         return "below";
54488                 }
54489     },
54490
54491     onNodeEnter : function(n, dd, e, data){
54492                 return false;
54493     },
54494     
54495     onNodeOver : function(n, dd, e, data){
54496                 var pt = this.getDropPoint(e, n, dd);
54497                 // set the insert point style on the target node
54498                 var dragElClass = this.dropNotAllowed;
54499                 if (pt) {
54500                         var targetElClass;
54501                         if (pt == "above"){
54502                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54503                                 targetElClass = "x-view-drag-insert-above";
54504                         } else {
54505                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54506                                 targetElClass = "x-view-drag-insert-below";
54507                         }
54508                         if (this.lastInsertClass != targetElClass){
54509                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54510                                 this.lastInsertClass = targetElClass;
54511                         }
54512                 }
54513                 return dragElClass;
54514         },
54515
54516     onNodeOut : function(n, dd, e, data){
54517                 this.removeDropIndicators(n);
54518     },
54519
54520     onNodeDrop : function(n, dd, e, data){
54521         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54522                 return false;
54523         }
54524         var pt = this.getDropPoint(e, n, dd);
54525                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54526                 if (pt == "below") { insertAt++; }
54527                 for (var i = 0; i < data.records.length; i++) {
54528                         var r = data.records[i];
54529                         var dup = this.store.getById(r.id);
54530                         if (dup && (dd != this.dragZone)) {
54531                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54532                         } else {
54533                                 if (data.copy) {
54534                                         this.store.insert(insertAt++, r.copy());
54535                                 } else {
54536                                         data.source.isDirtyFlag = true;
54537                                         r.store.remove(r);
54538                                         this.store.insert(insertAt++, r);
54539                                 }
54540                                 this.isDirtyFlag = true;
54541                         }
54542                 }
54543                 this.dragZone.cachedTarget = null;
54544                 return true;
54545     },
54546
54547     removeDropIndicators : function(n){
54548                 if(n){
54549                         Roo.fly(n).removeClass([
54550                                 "x-view-drag-insert-above",
54551                                 "x-view-drag-insert-below"]);
54552                         this.lastInsertClass = "_noclass";
54553                 }
54554     },
54555
54556 /**
54557  *      Utility method. Add a delete option to the DDView's context menu.
54558  *      @param {String} imageUrl The URL of the "delete" icon image.
54559  */
54560         setDeletable: function(imageUrl) {
54561                 if (!this.singleSelect && !this.multiSelect) {
54562                         this.singleSelect = true;
54563                 }
54564                 var c = this.getContextMenu();
54565                 this.contextMenu.on("itemclick", function(item) {
54566                         switch (item.id) {
54567                                 case "delete":
54568                                         this.remove(this.getSelectedIndexes());
54569                                         break;
54570                         }
54571                 }, this);
54572                 this.contextMenu.add({
54573                         icon: imageUrl,
54574                         id: "delete",
54575                         text: 'Delete'
54576                 });
54577         },
54578         
54579 /**     Return the context menu for this DDView. */
54580         getContextMenu: function() {
54581                 if (!this.contextMenu) {
54582 //                      Create the View's context menu
54583                         this.contextMenu = new Roo.menu.Menu({
54584                                 id: this.id + "-contextmenu"
54585                         });
54586                         this.el.on("contextmenu", this.showContextMenu, this);
54587                 }
54588                 return this.contextMenu;
54589         },
54590         
54591         disableContextMenu: function() {
54592                 if (this.contextMenu) {
54593                         this.el.un("contextmenu", this.showContextMenu, this);
54594                 }
54595         },
54596
54597         showContextMenu: function(e, item) {
54598         item = this.findItemFromChild(e.getTarget());
54599                 if (item) {
54600                         e.stopEvent();
54601                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54602                         this.contextMenu.showAt(e.getXY());
54603             }
54604     },
54605
54606 /**
54607  *      Remove {@link Roo.data.Record}s at the specified indices.
54608  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54609  */
54610     remove: function(selectedIndices) {
54611                 selectedIndices = [].concat(selectedIndices);
54612                 for (var i = 0; i < selectedIndices.length; i++) {
54613                         var rec = this.store.getAt(selectedIndices[i]);
54614                         this.store.remove(rec);
54615                 }
54616     },
54617
54618 /**
54619  *      Double click fires the event, but also, if this is draggable, and there is only one other
54620  *      related DropZone, it transfers the selected node.
54621  */
54622     onDblClick : function(e){
54623         var item = this.findItemFromChild(e.getTarget());
54624         if(item){
54625             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54626                 return false;
54627             }
54628             if (this.dragGroup) {
54629                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54630                     while (targets.indexOf(this.dropZone) > -1) {
54631                             targets.remove(this.dropZone);
54632                                 }
54633                     if (targets.length == 1) {
54634                                         this.dragZone.cachedTarget = null;
54635                         var el = Roo.get(targets[0].getEl());
54636                         var box = el.getBox(true);
54637                         targets[0].onNodeDrop(el.dom, {
54638                                 target: el.dom,
54639                                 xy: [box.x, box.y + box.height - 1]
54640                         }, null, this.getDragData(e));
54641                     }
54642                 }
54643         }
54644     },
54645     
54646     handleSelection: function(e) {
54647                 this.dragZone.cachedTarget = null;
54648         var item = this.findItemFromChild(e.getTarget());
54649         if (!item) {
54650                 this.clearSelections(true);
54651                 return;
54652         }
54653                 if (item && (this.multiSelect || this.singleSelect)){
54654                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54655                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54656                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54657                                 this.unselect(item);
54658                         } else {
54659                                 this.select(item, this.multiSelect && e.ctrlKey);
54660                                 this.lastSelection = item;
54661                         }
54662                 }
54663     },
54664
54665     onItemClick : function(item, index, e){
54666                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54667                         return false;
54668                 }
54669                 return true;
54670     },
54671
54672     unselect : function(nodeInfo, suppressEvent){
54673                 var node = this.getNode(nodeInfo);
54674                 if(node && this.isSelected(node)){
54675                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54676                                 Roo.fly(node).removeClass(this.selectedClass);
54677                                 this.selections.remove(node);
54678                                 if(!suppressEvent){
54679                                         this.fireEvent("selectionchange", this, this.selections);
54680                                 }
54681                         }
54682                 }
54683     }
54684 });
54685 /*
54686  * Based on:
54687  * Ext JS Library 1.1.1
54688  * Copyright(c) 2006-2007, Ext JS, LLC.
54689  *
54690  * Originally Released Under LGPL - original licence link has changed is not relivant.
54691  *
54692  * Fork - LGPL
54693  * <script type="text/javascript">
54694  */
54695  
54696 /**
54697  * @class Roo.LayoutManager
54698  * @extends Roo.util.Observable
54699  * Base class for layout managers.
54700  */
54701 Roo.LayoutManager = function(container, config){
54702     Roo.LayoutManager.superclass.constructor.call(this);
54703     this.el = Roo.get(container);
54704     // ie scrollbar fix
54705     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54706         document.body.scroll = "no";
54707     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54708         this.el.position('relative');
54709     }
54710     this.id = this.el.id;
54711     this.el.addClass("x-layout-container");
54712     /** false to disable window resize monitoring @type Boolean */
54713     this.monitorWindowResize = true;
54714     this.regions = {};
54715     this.addEvents({
54716         /**
54717          * @event layout
54718          * Fires when a layout is performed. 
54719          * @param {Roo.LayoutManager} this
54720          */
54721         "layout" : true,
54722         /**
54723          * @event regionresized
54724          * Fires when the user resizes a region. 
54725          * @param {Roo.LayoutRegion} region The resized region
54726          * @param {Number} newSize The new size (width for east/west, height for north/south)
54727          */
54728         "regionresized" : true,
54729         /**
54730          * @event regioncollapsed
54731          * Fires when a region is collapsed. 
54732          * @param {Roo.LayoutRegion} region The collapsed region
54733          */
54734         "regioncollapsed" : true,
54735         /**
54736          * @event regionexpanded
54737          * Fires when a region is expanded.  
54738          * @param {Roo.LayoutRegion} region The expanded region
54739          */
54740         "regionexpanded" : true
54741     });
54742     this.updating = false;
54743     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54744 };
54745
54746 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54747     /**
54748      * Returns true if this layout is currently being updated
54749      * @return {Boolean}
54750      */
54751     isUpdating : function(){
54752         return this.updating; 
54753     },
54754     
54755     /**
54756      * Suspend the LayoutManager from doing auto-layouts while
54757      * making multiple add or remove calls
54758      */
54759     beginUpdate : function(){
54760         this.updating = true;    
54761     },
54762     
54763     /**
54764      * Restore auto-layouts and optionally disable the manager from performing a layout
54765      * @param {Boolean} noLayout true to disable a layout update 
54766      */
54767     endUpdate : function(noLayout){
54768         this.updating = false;
54769         if(!noLayout){
54770             this.layout();
54771         }    
54772     },
54773     
54774     layout: function(){
54775         
54776     },
54777     
54778     onRegionResized : function(region, newSize){
54779         this.fireEvent("regionresized", region, newSize);
54780         this.layout();
54781     },
54782     
54783     onRegionCollapsed : function(region){
54784         this.fireEvent("regioncollapsed", region);
54785     },
54786     
54787     onRegionExpanded : function(region){
54788         this.fireEvent("regionexpanded", region);
54789     },
54790         
54791     /**
54792      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54793      * performs box-model adjustments.
54794      * @return {Object} The size as an object {width: (the width), height: (the height)}
54795      */
54796     getViewSize : function(){
54797         var size;
54798         if(this.el.dom != document.body){
54799             size = this.el.getSize();
54800         }else{
54801             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54802         }
54803         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54804         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54805         return size;
54806     },
54807     
54808     /**
54809      * Returns the Element this layout is bound to.
54810      * @return {Roo.Element}
54811      */
54812     getEl : function(){
54813         return this.el;
54814     },
54815     
54816     /**
54817      * Returns the specified region.
54818      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54819      * @return {Roo.LayoutRegion}
54820      */
54821     getRegion : function(target){
54822         return this.regions[target.toLowerCase()];
54823     },
54824     
54825     onWindowResize : function(){
54826         if(this.monitorWindowResize){
54827             this.layout();
54828         }
54829     }
54830 });/*
54831  * Based on:
54832  * Ext JS Library 1.1.1
54833  * Copyright(c) 2006-2007, Ext JS, LLC.
54834  *
54835  * Originally Released Under LGPL - original licence link has changed is not relivant.
54836  *
54837  * Fork - LGPL
54838  * <script type="text/javascript">
54839  */
54840 /**
54841  * @class Roo.BorderLayout
54842  * @extends Roo.LayoutManager
54843  * @children Roo.ContentPanel
54844  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54845  * please see: <br><br>
54846  * <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>
54847  * <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>
54848  * Example:
54849  <pre><code>
54850  var layout = new Roo.BorderLayout(document.body, {
54851     north: {
54852         initialSize: 25,
54853         titlebar: false
54854     },
54855     west: {
54856         split:true,
54857         initialSize: 200,
54858         minSize: 175,
54859         maxSize: 400,
54860         titlebar: true,
54861         collapsible: true
54862     },
54863     east: {
54864         split:true,
54865         initialSize: 202,
54866         minSize: 175,
54867         maxSize: 400,
54868         titlebar: true,
54869         collapsible: true
54870     },
54871     south: {
54872         split:true,
54873         initialSize: 100,
54874         minSize: 100,
54875         maxSize: 200,
54876         titlebar: true,
54877         collapsible: true
54878     },
54879     center: {
54880         titlebar: true,
54881         autoScroll:true,
54882         resizeTabs: true,
54883         minTabWidth: 50,
54884         preferredTabWidth: 150
54885     }
54886 });
54887
54888 // shorthand
54889 var CP = Roo.ContentPanel;
54890
54891 layout.beginUpdate();
54892 layout.add("north", new CP("north", "North"));
54893 layout.add("south", new CP("south", {title: "South", closable: true}));
54894 layout.add("west", new CP("west", {title: "West"}));
54895 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54896 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54897 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54898 layout.getRegion("center").showPanel("center1");
54899 layout.endUpdate();
54900 </code></pre>
54901
54902 <b>The container the layout is rendered into can be either the body element or any other element.
54903 If it is not the body element, the container needs to either be an absolute positioned element,
54904 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54905 the container size if it is not the body element.</b>
54906
54907 * @constructor
54908 * Create a new BorderLayout
54909 * @param {String/HTMLElement/Element} container The container this layout is bound to
54910 * @param {Object} config Configuration options
54911  */
54912 Roo.BorderLayout = function(container, config){
54913     config = config || {};
54914     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54915     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54916     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54917         var target = this.factory.validRegions[i];
54918         if(config[target]){
54919             this.addRegion(target, config[target]);
54920         }
54921     }
54922 };
54923
54924 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54925         
54926         /**
54927          * @cfg {Roo.LayoutRegion} east
54928          */
54929         /**
54930          * @cfg {Roo.LayoutRegion} west
54931          */
54932         /**
54933          * @cfg {Roo.LayoutRegion} north
54934          */
54935         /**
54936          * @cfg {Roo.LayoutRegion} south
54937          */
54938         /**
54939          * @cfg {Roo.LayoutRegion} center
54940          */
54941     /**
54942      * Creates and adds a new region if it doesn't already exist.
54943      * @param {String} target The target region key (north, south, east, west or center).
54944      * @param {Object} config The regions config object
54945      * @return {BorderLayoutRegion} The new region
54946      */
54947     addRegion : function(target, config){
54948         if(!this.regions[target]){
54949             var r = this.factory.create(target, this, config);
54950             this.bindRegion(target, r);
54951         }
54952         return this.regions[target];
54953     },
54954
54955     // private (kinda)
54956     bindRegion : function(name, r){
54957         this.regions[name] = r;
54958         r.on("visibilitychange", this.layout, this);
54959         r.on("paneladded", this.layout, this);
54960         r.on("panelremoved", this.layout, this);
54961         r.on("invalidated", this.layout, this);
54962         r.on("resized", this.onRegionResized, this);
54963         r.on("collapsed", this.onRegionCollapsed, this);
54964         r.on("expanded", this.onRegionExpanded, this);
54965     },
54966
54967     /**
54968      * Performs a layout update.
54969      */
54970     layout : function(){
54971         if(this.updating) {
54972             return;
54973         }
54974         var size = this.getViewSize();
54975         var w = size.width;
54976         var h = size.height;
54977         var centerW = w;
54978         var centerH = h;
54979         var centerY = 0;
54980         var centerX = 0;
54981         //var x = 0, y = 0;
54982
54983         var rs = this.regions;
54984         var north = rs["north"];
54985         var south = rs["south"]; 
54986         var west = rs["west"];
54987         var east = rs["east"];
54988         var center = rs["center"];
54989         //if(this.hideOnLayout){ // not supported anymore
54990             //c.el.setStyle("display", "none");
54991         //}
54992         if(north && north.isVisible()){
54993             var b = north.getBox();
54994             var m = north.getMargins();
54995             b.width = w - (m.left+m.right);
54996             b.x = m.left;
54997             b.y = m.top;
54998             centerY = b.height + b.y + m.bottom;
54999             centerH -= centerY;
55000             north.updateBox(this.safeBox(b));
55001         }
55002         if(south && south.isVisible()){
55003             var b = south.getBox();
55004             var m = south.getMargins();
55005             b.width = w - (m.left+m.right);
55006             b.x = m.left;
55007             var totalHeight = (b.height + m.top + m.bottom);
55008             b.y = h - totalHeight + m.top;
55009             centerH -= totalHeight;
55010             south.updateBox(this.safeBox(b));
55011         }
55012         if(west && west.isVisible()){
55013             var b = west.getBox();
55014             var m = west.getMargins();
55015             b.height = centerH - (m.top+m.bottom);
55016             b.x = m.left;
55017             b.y = centerY + m.top;
55018             var totalWidth = (b.width + m.left + m.right);
55019             centerX += totalWidth;
55020             centerW -= totalWidth;
55021             west.updateBox(this.safeBox(b));
55022         }
55023         if(east && east.isVisible()){
55024             var b = east.getBox();
55025             var m = east.getMargins();
55026             b.height = centerH - (m.top+m.bottom);
55027             var totalWidth = (b.width + m.left + m.right);
55028             b.x = w - totalWidth + m.left;
55029             b.y = centerY + m.top;
55030             centerW -= totalWidth;
55031             east.updateBox(this.safeBox(b));
55032         }
55033         if(center){
55034             var m = center.getMargins();
55035             var centerBox = {
55036                 x: centerX + m.left,
55037                 y: centerY + m.top,
55038                 width: centerW - (m.left+m.right),
55039                 height: centerH - (m.top+m.bottom)
55040             };
55041             //if(this.hideOnLayout){
55042                 //center.el.setStyle("display", "block");
55043             //}
55044             center.updateBox(this.safeBox(centerBox));
55045         }
55046         this.el.repaint();
55047         this.fireEvent("layout", this);
55048     },
55049
55050     // private
55051     safeBox : function(box){
55052         box.width = Math.max(0, box.width);
55053         box.height = Math.max(0, box.height);
55054         return box;
55055     },
55056
55057     /**
55058      * Adds a ContentPanel (or subclass) to this layout.
55059      * @param {String} target The target region key (north, south, east, west or center).
55060      * @param {Roo.ContentPanel} panel The panel to add
55061      * @return {Roo.ContentPanel} The added panel
55062      */
55063     add : function(target, panel){
55064          
55065         target = target.toLowerCase();
55066         return this.regions[target].add(panel);
55067     },
55068
55069     /**
55070      * Remove a ContentPanel (or subclass) to this layout.
55071      * @param {String} target The target region key (north, south, east, west or center).
55072      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55073      * @return {Roo.ContentPanel} The removed panel
55074      */
55075     remove : function(target, panel){
55076         target = target.toLowerCase();
55077         return this.regions[target].remove(panel);
55078     },
55079
55080     /**
55081      * Searches all regions for a panel with the specified id
55082      * @param {String} panelId
55083      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55084      */
55085     findPanel : function(panelId){
55086         var rs = this.regions;
55087         for(var target in rs){
55088             if(typeof rs[target] != "function"){
55089                 var p = rs[target].getPanel(panelId);
55090                 if(p){
55091                     return p;
55092                 }
55093             }
55094         }
55095         return null;
55096     },
55097
55098     /**
55099      * Searches all regions for a panel with the specified id and activates (shows) it.
55100      * @param {String/ContentPanel} panelId The panels id or the panel itself
55101      * @return {Roo.ContentPanel} The shown panel or null
55102      */
55103     showPanel : function(panelId) {
55104       var rs = this.regions;
55105       for(var target in rs){
55106          var r = rs[target];
55107          if(typeof r != "function"){
55108             if(r.hasPanel(panelId)){
55109                return r.showPanel(panelId);
55110             }
55111          }
55112       }
55113       return null;
55114    },
55115
55116    /**
55117      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55118      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55119      */
55120     restoreState : function(provider){
55121         if(!provider){
55122             provider = Roo.state.Manager;
55123         }
55124         var sm = new Roo.LayoutStateManager();
55125         sm.init(this, provider);
55126     },
55127
55128     /**
55129      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55130      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55131      * a valid ContentPanel config object.  Example:
55132      * <pre><code>
55133 // Create the main layout
55134 var layout = new Roo.BorderLayout('main-ct', {
55135     west: {
55136         split:true,
55137         minSize: 175,
55138         titlebar: true
55139     },
55140     center: {
55141         title:'Components'
55142     }
55143 }, 'main-ct');
55144
55145 // Create and add multiple ContentPanels at once via configs
55146 layout.batchAdd({
55147    west: {
55148        id: 'source-files',
55149        autoCreate:true,
55150        title:'Ext Source Files',
55151        autoScroll:true,
55152        fitToFrame:true
55153    },
55154    center : {
55155        el: cview,
55156        autoScroll:true,
55157        fitToFrame:true,
55158        toolbar: tb,
55159        resizeEl:'cbody'
55160    }
55161 });
55162 </code></pre>
55163      * @param {Object} regions An object containing ContentPanel configs by region name
55164      */
55165     batchAdd : function(regions){
55166         this.beginUpdate();
55167         for(var rname in regions){
55168             var lr = this.regions[rname];
55169             if(lr){
55170                 this.addTypedPanels(lr, regions[rname]);
55171             }
55172         }
55173         this.endUpdate();
55174     },
55175
55176     // private
55177     addTypedPanels : function(lr, ps){
55178         if(typeof ps == 'string'){
55179             lr.add(new Roo.ContentPanel(ps));
55180         }
55181         else if(ps instanceof Array){
55182             for(var i =0, len = ps.length; i < len; i++){
55183                 this.addTypedPanels(lr, ps[i]);
55184             }
55185         }
55186         else if(!ps.events){ // raw config?
55187             var el = ps.el;
55188             delete ps.el; // prevent conflict
55189             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55190         }
55191         else {  // panel object assumed!
55192             lr.add(ps);
55193         }
55194     },
55195     /**
55196      * Adds a xtype elements to the layout.
55197      * <pre><code>
55198
55199 layout.addxtype({
55200        xtype : 'ContentPanel',
55201        region: 'west',
55202        items: [ .... ]
55203    }
55204 );
55205
55206 layout.addxtype({
55207         xtype : 'NestedLayoutPanel',
55208         region: 'west',
55209         layout: {
55210            center: { },
55211            west: { }   
55212         },
55213         items : [ ... list of content panels or nested layout panels.. ]
55214    }
55215 );
55216 </code></pre>
55217      * @param {Object} cfg Xtype definition of item to add.
55218      */
55219     addxtype : function(cfg)
55220     {
55221         // basically accepts a pannel...
55222         // can accept a layout region..!?!?
55223         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55224         
55225         if (!cfg.xtype.match(/Panel$/)) {
55226             return false;
55227         }
55228         var ret = false;
55229         
55230         if (typeof(cfg.region) == 'undefined') {
55231             Roo.log("Failed to add Panel, region was not set");
55232             Roo.log(cfg);
55233             return false;
55234         }
55235         var region = cfg.region;
55236         delete cfg.region;
55237         
55238           
55239         var xitems = [];
55240         if (cfg.items) {
55241             xitems = cfg.items;
55242             delete cfg.items;
55243         }
55244         var nb = false;
55245         
55246         switch(cfg.xtype) 
55247         {
55248             case 'ContentPanel':  // ContentPanel (el, cfg)
55249             case 'ScrollPanel':  // ContentPanel (el, cfg)
55250             case 'ViewPanel': 
55251                 if(cfg.autoCreate) {
55252                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55253                 } else {
55254                     var el = this.el.createChild();
55255                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55256                 }
55257                 
55258                 this.add(region, ret);
55259                 break;
55260             
55261             
55262             case 'TreePanel': // our new panel!
55263                 cfg.el = this.el.createChild();
55264                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55265                 this.add(region, ret);
55266                 break;
55267             
55268             case 'NestedLayoutPanel': 
55269                 // create a new Layout (which is  a Border Layout...
55270                 var el = this.el.createChild();
55271                 var clayout = cfg.layout;
55272                 delete cfg.layout;
55273                 clayout.items   = clayout.items  || [];
55274                 // replace this exitems with the clayout ones..
55275                 xitems = clayout.items;
55276                  
55277                 
55278                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55279                     cfg.background = false;
55280                 }
55281                 var layout = new Roo.BorderLayout(el, clayout);
55282                 
55283                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55284                 //console.log('adding nested layout panel '  + cfg.toSource());
55285                 this.add(region, ret);
55286                 nb = {}; /// find first...
55287                 break;
55288                 
55289             case 'GridPanel': 
55290             
55291                 // needs grid and region
55292                 
55293                 //var el = this.getRegion(region).el.createChild();
55294                 var el = this.el.createChild();
55295                 // create the grid first...
55296                 
55297                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55298                 delete cfg.grid;
55299                 if (region == 'center' && this.active ) {
55300                     cfg.background = false;
55301                 }
55302                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55303                 
55304                 this.add(region, ret);
55305                 if (cfg.background) {
55306                     ret.on('activate', function(gp) {
55307                         if (!gp.grid.rendered) {
55308                             gp.grid.render();
55309                         }
55310                     });
55311                 } else {
55312                     grid.render();
55313                 }
55314                 break;
55315            
55316            
55317            
55318                 
55319                 
55320                 
55321             default:
55322                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55323                     
55324                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55325                     this.add(region, ret);
55326                 } else {
55327                 
55328                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55329                     return null;
55330                 }
55331                 
55332              // GridPanel (grid, cfg)
55333             
55334         }
55335         this.beginUpdate();
55336         // add children..
55337         var region = '';
55338         var abn = {};
55339         Roo.each(xitems, function(i)  {
55340             region = nb && i.region ? i.region : false;
55341             
55342             var add = ret.addxtype(i);
55343            
55344             if (region) {
55345                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55346                 if (!i.background) {
55347                     abn[region] = nb[region] ;
55348                 }
55349             }
55350             
55351         });
55352         this.endUpdate();
55353
55354         // make the last non-background panel active..
55355         //if (nb) { Roo.log(abn); }
55356         if (nb) {
55357             
55358             for(var r in abn) {
55359                 region = this.getRegion(r);
55360                 if (region) {
55361                     // tried using nb[r], but it does not work..
55362                      
55363                     region.showPanel(abn[r]);
55364                    
55365                 }
55366             }
55367         }
55368         return ret;
55369         
55370     }
55371 });
55372
55373 /**
55374  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55375  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55376  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55377  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55378  * <pre><code>
55379 // shorthand
55380 var CP = Roo.ContentPanel;
55381
55382 var layout = Roo.BorderLayout.create({
55383     north: {
55384         initialSize: 25,
55385         titlebar: false,
55386         panels: [new CP("north", "North")]
55387     },
55388     west: {
55389         split:true,
55390         initialSize: 200,
55391         minSize: 175,
55392         maxSize: 400,
55393         titlebar: true,
55394         collapsible: true,
55395         panels: [new CP("west", {title: "West"})]
55396     },
55397     east: {
55398         split:true,
55399         initialSize: 202,
55400         minSize: 175,
55401         maxSize: 400,
55402         titlebar: true,
55403         collapsible: true,
55404         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55405     },
55406     south: {
55407         split:true,
55408         initialSize: 100,
55409         minSize: 100,
55410         maxSize: 200,
55411         titlebar: true,
55412         collapsible: true,
55413         panels: [new CP("south", {title: "South", closable: true})]
55414     },
55415     center: {
55416         titlebar: true,
55417         autoScroll:true,
55418         resizeTabs: true,
55419         minTabWidth: 50,
55420         preferredTabWidth: 150,
55421         panels: [
55422             new CP("center1", {title: "Close Me", closable: true}),
55423             new CP("center2", {title: "Center Panel", closable: false})
55424         ]
55425     }
55426 }, document.body);
55427
55428 layout.getRegion("center").showPanel("center1");
55429 </code></pre>
55430  * @param config
55431  * @param targetEl
55432  */
55433 Roo.BorderLayout.create = function(config, targetEl){
55434     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55435     layout.beginUpdate();
55436     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55437     for(var j = 0, jlen = regions.length; j < jlen; j++){
55438         var lr = regions[j];
55439         if(layout.regions[lr] && config[lr].panels){
55440             var r = layout.regions[lr];
55441             var ps = config[lr].panels;
55442             layout.addTypedPanels(r, ps);
55443         }
55444     }
55445     layout.endUpdate();
55446     return layout;
55447 };
55448
55449 // private
55450 Roo.BorderLayout.RegionFactory = {
55451     // private
55452     validRegions : ["north","south","east","west","center"],
55453
55454     // private
55455     create : function(target, mgr, config){
55456         target = target.toLowerCase();
55457         if(config.lightweight || config.basic){
55458             return new Roo.BasicLayoutRegion(mgr, config, target);
55459         }
55460         switch(target){
55461             case "north":
55462                 return new Roo.NorthLayoutRegion(mgr, config);
55463             case "south":
55464                 return new Roo.SouthLayoutRegion(mgr, config);
55465             case "east":
55466                 return new Roo.EastLayoutRegion(mgr, config);
55467             case "west":
55468                 return new Roo.WestLayoutRegion(mgr, config);
55469             case "center":
55470                 return new Roo.CenterLayoutRegion(mgr, config);
55471         }
55472         throw 'Layout region "'+target+'" not supported.';
55473     }
55474 };/*
55475  * Based on:
55476  * Ext JS Library 1.1.1
55477  * Copyright(c) 2006-2007, Ext JS, LLC.
55478  *
55479  * Originally Released Under LGPL - original licence link has changed is not relivant.
55480  *
55481  * Fork - LGPL
55482  * <script type="text/javascript">
55483  */
55484  
55485 /**
55486  * @class Roo.BasicLayoutRegion
55487  * @extends Roo.util.Observable
55488  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55489  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55490  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55491  */
55492 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55493     this.mgr = mgr;
55494     this.position  = pos;
55495     this.events = {
55496         /**
55497          * @scope Roo.BasicLayoutRegion
55498          */
55499         
55500         /**
55501          * @event beforeremove
55502          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55503          * @param {Roo.LayoutRegion} this
55504          * @param {Roo.ContentPanel} panel The panel
55505          * @param {Object} e The cancel event object
55506          */
55507         "beforeremove" : true,
55508         /**
55509          * @event invalidated
55510          * Fires when the layout for this region is changed.
55511          * @param {Roo.LayoutRegion} this
55512          */
55513         "invalidated" : true,
55514         /**
55515          * @event visibilitychange
55516          * Fires when this region is shown or hidden 
55517          * @param {Roo.LayoutRegion} this
55518          * @param {Boolean} visibility true or false
55519          */
55520         "visibilitychange" : true,
55521         /**
55522          * @event paneladded
55523          * Fires when a panel is added. 
55524          * @param {Roo.LayoutRegion} this
55525          * @param {Roo.ContentPanel} panel The panel
55526          */
55527         "paneladded" : true,
55528         /**
55529          * @event panelremoved
55530          * Fires when a panel is removed. 
55531          * @param {Roo.LayoutRegion} this
55532          * @param {Roo.ContentPanel} panel The panel
55533          */
55534         "panelremoved" : true,
55535         /**
55536          * @event beforecollapse
55537          * Fires when this region before collapse.
55538          * @param {Roo.LayoutRegion} this
55539          */
55540         "beforecollapse" : true,
55541         /**
55542          * @event collapsed
55543          * Fires when this region is collapsed.
55544          * @param {Roo.LayoutRegion} this
55545          */
55546         "collapsed" : true,
55547         /**
55548          * @event expanded
55549          * Fires when this region is expanded.
55550          * @param {Roo.LayoutRegion} this
55551          */
55552         "expanded" : true,
55553         /**
55554          * @event slideshow
55555          * Fires when this region is slid into view.
55556          * @param {Roo.LayoutRegion} this
55557          */
55558         "slideshow" : true,
55559         /**
55560          * @event slidehide
55561          * Fires when this region slides out of view. 
55562          * @param {Roo.LayoutRegion} this
55563          */
55564         "slidehide" : true,
55565         /**
55566          * @event panelactivated
55567          * Fires when a panel is activated. 
55568          * @param {Roo.LayoutRegion} this
55569          * @param {Roo.ContentPanel} panel The activated panel
55570          */
55571         "panelactivated" : true,
55572         /**
55573          * @event resized
55574          * Fires when the user resizes this region. 
55575          * @param {Roo.LayoutRegion} this
55576          * @param {Number} newSize The new size (width for east/west, height for north/south)
55577          */
55578         "resized" : true
55579     };
55580     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55581     this.panels = new Roo.util.MixedCollection();
55582     this.panels.getKey = this.getPanelId.createDelegate(this);
55583     this.box = null;
55584     this.activePanel = null;
55585     // ensure listeners are added...
55586     
55587     if (config.listeners || config.events) {
55588         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55589             listeners : config.listeners || {},
55590             events : config.events || {}
55591         });
55592     }
55593     
55594     if(skipConfig !== true){
55595         this.applyConfig(config);
55596     }
55597 };
55598
55599 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55600     getPanelId : function(p){
55601         return p.getId();
55602     },
55603     
55604     applyConfig : function(config){
55605         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55606         this.config = config;
55607         
55608     },
55609     
55610     /**
55611      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55612      * the width, for horizontal (north, south) the height.
55613      * @param {Number} newSize The new width or height
55614      */
55615     resizeTo : function(newSize){
55616         var el = this.el ? this.el :
55617                  (this.activePanel ? this.activePanel.getEl() : null);
55618         if(el){
55619             switch(this.position){
55620                 case "east":
55621                 case "west":
55622                     el.setWidth(newSize);
55623                     this.fireEvent("resized", this, newSize);
55624                 break;
55625                 case "north":
55626                 case "south":
55627                     el.setHeight(newSize);
55628                     this.fireEvent("resized", this, newSize);
55629                 break;                
55630             }
55631         }
55632     },
55633     
55634     getBox : function(){
55635         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55636     },
55637     
55638     getMargins : function(){
55639         return this.margins;
55640     },
55641     
55642     updateBox : function(box){
55643         this.box = box;
55644         var el = this.activePanel.getEl();
55645         el.dom.style.left = box.x + "px";
55646         el.dom.style.top = box.y + "px";
55647         this.activePanel.setSize(box.width, box.height);
55648     },
55649     
55650     /**
55651      * Returns the container element for this region.
55652      * @return {Roo.Element}
55653      */
55654     getEl : function(){
55655         return this.activePanel;
55656     },
55657     
55658     /**
55659      * Returns true if this region is currently visible.
55660      * @return {Boolean}
55661      */
55662     isVisible : function(){
55663         return this.activePanel ? true : false;
55664     },
55665     
55666     setActivePanel : function(panel){
55667         panel = this.getPanel(panel);
55668         if(this.activePanel && this.activePanel != panel){
55669             this.activePanel.setActiveState(false);
55670             this.activePanel.getEl().setLeftTop(-10000,-10000);
55671         }
55672         this.activePanel = panel;
55673         panel.setActiveState(true);
55674         if(this.box){
55675             panel.setSize(this.box.width, this.box.height);
55676         }
55677         this.fireEvent("panelactivated", this, panel);
55678         this.fireEvent("invalidated");
55679     },
55680     
55681     /**
55682      * Show the specified panel.
55683      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55684      * @return {Roo.ContentPanel} The shown panel or null
55685      */
55686     showPanel : function(panel){
55687         if(panel = this.getPanel(panel)){
55688             this.setActivePanel(panel);
55689         }
55690         return panel;
55691     },
55692     
55693     /**
55694      * Get the active panel for this region.
55695      * @return {Roo.ContentPanel} The active panel or null
55696      */
55697     getActivePanel : function(){
55698         return this.activePanel;
55699     },
55700     
55701     /**
55702      * Add the passed ContentPanel(s)
55703      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55704      * @return {Roo.ContentPanel} The panel added (if only one was added)
55705      */
55706     add : function(panel){
55707         if(arguments.length > 1){
55708             for(var i = 0, len = arguments.length; i < len; i++) {
55709                 this.add(arguments[i]);
55710             }
55711             return null;
55712         }
55713         if(this.hasPanel(panel)){
55714             this.showPanel(panel);
55715             return panel;
55716         }
55717         var el = panel.getEl();
55718         if(el.dom.parentNode != this.mgr.el.dom){
55719             this.mgr.el.dom.appendChild(el.dom);
55720         }
55721         if(panel.setRegion){
55722             panel.setRegion(this);
55723         }
55724         this.panels.add(panel);
55725         el.setStyle("position", "absolute");
55726         if(!panel.background){
55727             this.setActivePanel(panel);
55728             if(this.config.initialSize && this.panels.getCount()==1){
55729                 this.resizeTo(this.config.initialSize);
55730             }
55731         }
55732         this.fireEvent("paneladded", this, panel);
55733         return panel;
55734     },
55735     
55736     /**
55737      * Returns true if the panel is in this region.
55738      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55739      * @return {Boolean}
55740      */
55741     hasPanel : function(panel){
55742         if(typeof panel == "object"){ // must be panel obj
55743             panel = panel.getId();
55744         }
55745         return this.getPanel(panel) ? true : false;
55746     },
55747     
55748     /**
55749      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55750      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55751      * @param {Boolean} preservePanel Overrides the config preservePanel option
55752      * @return {Roo.ContentPanel} The panel that was removed
55753      */
55754     remove : function(panel, preservePanel){
55755         panel = this.getPanel(panel);
55756         if(!panel){
55757             return null;
55758         }
55759         var e = {};
55760         this.fireEvent("beforeremove", this, panel, e);
55761         if(e.cancel === true){
55762             return null;
55763         }
55764         var panelId = panel.getId();
55765         this.panels.removeKey(panelId);
55766         return panel;
55767     },
55768     
55769     /**
55770      * Returns the panel specified or null if it's not in this region.
55771      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55772      * @return {Roo.ContentPanel}
55773      */
55774     getPanel : function(id){
55775         if(typeof id == "object"){ // must be panel obj
55776             return id;
55777         }
55778         return this.panels.get(id);
55779     },
55780     
55781     /**
55782      * Returns this regions position (north/south/east/west/center).
55783      * @return {String} 
55784      */
55785     getPosition: function(){
55786         return this.position;    
55787     }
55788 });/*
55789  * Based on:
55790  * Ext JS Library 1.1.1
55791  * Copyright(c) 2006-2007, Ext JS, LLC.
55792  *
55793  * Originally Released Under LGPL - original licence link has changed is not relivant.
55794  *
55795  * Fork - LGPL
55796  * <script type="text/javascript">
55797  */
55798  
55799 /**
55800  * @class Roo.LayoutRegion
55801  * @extends Roo.BasicLayoutRegion
55802  * This class represents a region in a layout manager.
55803  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55804  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55805  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55806  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55807  * @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})
55808  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55809  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55810  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55811  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55812  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55813  * @cfg {String}    title           The title for the region (overrides panel titles)
55814  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55815  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55816  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55817  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55818  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55819  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55820  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55821  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55822  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55823  * @cfg {Boolean}   showPin         True to show a pin button
55824  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55825  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55826  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55827  * @cfg {Number}    width           For East/West panels
55828  * @cfg {Number}    height          For North/South panels
55829  * @cfg {Boolean}   split           To show the splitter
55830  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55831  */
55832 Roo.LayoutRegion = function(mgr, config, pos){
55833     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55834     var dh = Roo.DomHelper;
55835     /** This region's container element 
55836     * @type Roo.Element */
55837     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55838     /** This region's title element 
55839     * @type Roo.Element */
55840
55841     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55842         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55843         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55844     ]}, true);
55845     this.titleEl.enableDisplayMode();
55846     /** This region's title text element 
55847     * @type HTMLElement */
55848     this.titleTextEl = this.titleEl.dom.firstChild;
55849     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55850     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55851     this.closeBtn.enableDisplayMode();
55852     this.closeBtn.on("click", this.closeClicked, this);
55853     this.closeBtn.hide();
55854
55855     this.createBody(config);
55856     this.visible = true;
55857     this.collapsed = false;
55858
55859     if(config.hideWhenEmpty){
55860         this.hide();
55861         this.on("paneladded", this.validateVisibility, this);
55862         this.on("panelremoved", this.validateVisibility, this);
55863     }
55864     this.applyConfig(config);
55865 };
55866
55867 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55868
55869     createBody : function(){
55870         /** This region's body element 
55871         * @type Roo.Element */
55872         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55873     },
55874
55875     applyConfig : function(c){
55876         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55877             var dh = Roo.DomHelper;
55878             if(c.titlebar !== false){
55879                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55880                 this.collapseBtn.on("click", this.collapse, this);
55881                 this.collapseBtn.enableDisplayMode();
55882
55883                 if(c.showPin === true || this.showPin){
55884                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55885                     this.stickBtn.enableDisplayMode();
55886                     this.stickBtn.on("click", this.expand, this);
55887                     this.stickBtn.hide();
55888                 }
55889             }
55890             /** This region's collapsed element
55891             * @type Roo.Element */
55892             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55893                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55894             ]}, true);
55895             if(c.floatable !== false){
55896                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55897                this.collapsedEl.on("click", this.collapseClick, this);
55898             }
55899
55900             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55901                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55902                    id: "message", unselectable: "on", style:{"float":"left"}});
55903                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55904              }
55905             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55906             this.expandBtn.on("click", this.expand, this);
55907         }
55908         if(this.collapseBtn){
55909             this.collapseBtn.setVisible(c.collapsible == true);
55910         }
55911         this.cmargins = c.cmargins || this.cmargins ||
55912                          (this.position == "west" || this.position == "east" ?
55913                              {top: 0, left: 2, right:2, bottom: 0} :
55914                              {top: 2, left: 0, right:0, bottom: 2});
55915         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55916         this.bottomTabs = c.tabPosition != "top";
55917         this.autoScroll = c.autoScroll || false;
55918         if(this.autoScroll){
55919             this.bodyEl.setStyle("overflow", "auto");
55920         }else{
55921             this.bodyEl.setStyle("overflow", "hidden");
55922         }
55923         //if(c.titlebar !== false){
55924             if((!c.titlebar && !c.title) || c.titlebar === false){
55925                 this.titleEl.hide();
55926             }else{
55927                 this.titleEl.show();
55928                 if(c.title){
55929                     this.titleTextEl.innerHTML = c.title;
55930                 }
55931             }
55932         //}
55933         this.duration = c.duration || .30;
55934         this.slideDuration = c.slideDuration || .45;
55935         this.config = c;
55936         if(c.collapsed){
55937             this.collapse(true);
55938         }
55939         if(c.hidden){
55940             this.hide();
55941         }
55942     },
55943     /**
55944      * Returns true if this region is currently visible.
55945      * @return {Boolean}
55946      */
55947     isVisible : function(){
55948         return this.visible;
55949     },
55950
55951     /**
55952      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55953      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55954      */
55955     setCollapsedTitle : function(title){
55956         title = title || "&#160;";
55957         if(this.collapsedTitleTextEl){
55958             this.collapsedTitleTextEl.innerHTML = title;
55959         }
55960     },
55961
55962     getBox : function(){
55963         var b;
55964         if(!this.collapsed){
55965             b = this.el.getBox(false, true);
55966         }else{
55967             b = this.collapsedEl.getBox(false, true);
55968         }
55969         return b;
55970     },
55971
55972     getMargins : function(){
55973         return this.collapsed ? this.cmargins : this.margins;
55974     },
55975
55976     highlight : function(){
55977         this.el.addClass("x-layout-panel-dragover");
55978     },
55979
55980     unhighlight : function(){
55981         this.el.removeClass("x-layout-panel-dragover");
55982     },
55983
55984     updateBox : function(box){
55985         this.box = box;
55986         if(!this.collapsed){
55987             this.el.dom.style.left = box.x + "px";
55988             this.el.dom.style.top = box.y + "px";
55989             this.updateBody(box.width, box.height);
55990         }else{
55991             this.collapsedEl.dom.style.left = box.x + "px";
55992             this.collapsedEl.dom.style.top = box.y + "px";
55993             this.collapsedEl.setSize(box.width, box.height);
55994         }
55995         if(this.tabs){
55996             this.tabs.autoSizeTabs();
55997         }
55998     },
55999
56000     updateBody : function(w, h){
56001         if(w !== null){
56002             this.el.setWidth(w);
56003             w -= this.el.getBorderWidth("rl");
56004             if(this.config.adjustments){
56005                 w += this.config.adjustments[0];
56006             }
56007         }
56008         if(h !== null){
56009             this.el.setHeight(h);
56010             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56011             h -= this.el.getBorderWidth("tb");
56012             if(this.config.adjustments){
56013                 h += this.config.adjustments[1];
56014             }
56015             this.bodyEl.setHeight(h);
56016             if(this.tabs){
56017                 h = this.tabs.syncHeight(h);
56018             }
56019         }
56020         if(this.panelSize){
56021             w = w !== null ? w : this.panelSize.width;
56022             h = h !== null ? h : this.panelSize.height;
56023         }
56024         if(this.activePanel){
56025             var el = this.activePanel.getEl();
56026             w = w !== null ? w : el.getWidth();
56027             h = h !== null ? h : el.getHeight();
56028             this.panelSize = {width: w, height: h};
56029             this.activePanel.setSize(w, h);
56030         }
56031         if(Roo.isIE && this.tabs){
56032             this.tabs.el.repaint();
56033         }
56034     },
56035
56036     /**
56037      * Returns the container element for this region.
56038      * @return {Roo.Element}
56039      */
56040     getEl : function(){
56041         return this.el;
56042     },
56043
56044     /**
56045      * Hides this region.
56046      */
56047     hide : function(){
56048         if(!this.collapsed){
56049             this.el.dom.style.left = "-2000px";
56050             this.el.hide();
56051         }else{
56052             this.collapsedEl.dom.style.left = "-2000px";
56053             this.collapsedEl.hide();
56054         }
56055         this.visible = false;
56056         this.fireEvent("visibilitychange", this, false);
56057     },
56058
56059     /**
56060      * Shows this region if it was previously hidden.
56061      */
56062     show : function(){
56063         if(!this.collapsed){
56064             this.el.show();
56065         }else{
56066             this.collapsedEl.show();
56067         }
56068         this.visible = true;
56069         this.fireEvent("visibilitychange", this, true);
56070     },
56071
56072     closeClicked : function(){
56073         if(this.activePanel){
56074             this.remove(this.activePanel);
56075         }
56076     },
56077
56078     collapseClick : function(e){
56079         if(this.isSlid){
56080            e.stopPropagation();
56081            this.slideIn();
56082         }else{
56083            e.stopPropagation();
56084            this.slideOut();
56085         }
56086     },
56087
56088     /**
56089      * Collapses this region.
56090      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56091      */
56092     collapse : function(skipAnim, skipCheck){
56093         if(this.collapsed) {
56094             return;
56095         }
56096         
56097         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56098             
56099             this.collapsed = true;
56100             if(this.split){
56101                 this.split.el.hide();
56102             }
56103             if(this.config.animate && skipAnim !== true){
56104                 this.fireEvent("invalidated", this);
56105                 this.animateCollapse();
56106             }else{
56107                 this.el.setLocation(-20000,-20000);
56108                 this.el.hide();
56109                 this.collapsedEl.show();
56110                 this.fireEvent("collapsed", this);
56111                 this.fireEvent("invalidated", this);
56112             }
56113         }
56114         
56115     },
56116
56117     animateCollapse : function(){
56118         // overridden
56119     },
56120
56121     /**
56122      * Expands this region if it was previously collapsed.
56123      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56124      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56125      */
56126     expand : function(e, skipAnim){
56127         if(e) {
56128             e.stopPropagation();
56129         }
56130         if(!this.collapsed || this.el.hasActiveFx()) {
56131             return;
56132         }
56133         if(this.isSlid){
56134             this.afterSlideIn();
56135             skipAnim = true;
56136         }
56137         this.collapsed = false;
56138         if(this.config.animate && skipAnim !== true){
56139             this.animateExpand();
56140         }else{
56141             this.el.show();
56142             if(this.split){
56143                 this.split.el.show();
56144             }
56145             this.collapsedEl.setLocation(-2000,-2000);
56146             this.collapsedEl.hide();
56147             this.fireEvent("invalidated", this);
56148             this.fireEvent("expanded", this);
56149         }
56150     },
56151
56152     animateExpand : function(){
56153         // overridden
56154     },
56155
56156     initTabs : function()
56157     {
56158         this.bodyEl.setStyle("overflow", "hidden");
56159         var ts = new Roo.TabPanel(
56160                 this.bodyEl.dom,
56161                 {
56162                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56163                     disableTooltips: this.config.disableTabTips,
56164                     toolbar : this.config.toolbar
56165                 }
56166         );
56167         if(this.config.hideTabs){
56168             ts.stripWrap.setDisplayed(false);
56169         }
56170         this.tabs = ts;
56171         ts.resizeTabs = this.config.resizeTabs === true;
56172         ts.minTabWidth = this.config.minTabWidth || 40;
56173         ts.maxTabWidth = this.config.maxTabWidth || 250;
56174         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56175         ts.monitorResize = false;
56176         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56177         ts.bodyEl.addClass('x-layout-tabs-body');
56178         this.panels.each(this.initPanelAsTab, this);
56179     },
56180
56181     initPanelAsTab : function(panel){
56182         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56183                     this.config.closeOnTab && panel.isClosable());
56184         if(panel.tabTip !== undefined){
56185             ti.setTooltip(panel.tabTip);
56186         }
56187         ti.on("activate", function(){
56188               this.setActivePanel(panel);
56189         }, this);
56190         if(this.config.closeOnTab){
56191             ti.on("beforeclose", function(t, e){
56192                 e.cancel = true;
56193                 this.remove(panel);
56194             }, this);
56195         }
56196         return ti;
56197     },
56198
56199     updatePanelTitle : function(panel, title){
56200         if(this.activePanel == panel){
56201             this.updateTitle(title);
56202         }
56203         if(this.tabs){
56204             var ti = this.tabs.getTab(panel.getEl().id);
56205             ti.setText(title);
56206             if(panel.tabTip !== undefined){
56207                 ti.setTooltip(panel.tabTip);
56208             }
56209         }
56210     },
56211
56212     updateTitle : function(title){
56213         if(this.titleTextEl && !this.config.title){
56214             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56215         }
56216     },
56217
56218     setActivePanel : function(panel){
56219         panel = this.getPanel(panel);
56220         if(this.activePanel && this.activePanel != panel){
56221             this.activePanel.setActiveState(false);
56222         }
56223         this.activePanel = panel;
56224         panel.setActiveState(true);
56225         if(this.panelSize){
56226             panel.setSize(this.panelSize.width, this.panelSize.height);
56227         }
56228         if(this.closeBtn){
56229             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56230         }
56231         this.updateTitle(panel.getTitle());
56232         if(this.tabs){
56233             this.fireEvent("invalidated", this);
56234         }
56235         this.fireEvent("panelactivated", this, panel);
56236     },
56237
56238     /**
56239      * Shows the specified panel.
56240      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56241      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56242      */
56243     showPanel : function(panel)
56244     {
56245         panel = this.getPanel(panel);
56246         if(panel){
56247             if(this.tabs){
56248                 var tab = this.tabs.getTab(panel.getEl().id);
56249                 if(tab.isHidden()){
56250                     this.tabs.unhideTab(tab.id);
56251                 }
56252                 tab.activate();
56253             }else{
56254                 this.setActivePanel(panel);
56255             }
56256         }
56257         return panel;
56258     },
56259
56260     /**
56261      * Get the active panel for this region.
56262      * @return {Roo.ContentPanel} The active panel or null
56263      */
56264     getActivePanel : function(){
56265         return this.activePanel;
56266     },
56267
56268     validateVisibility : function(){
56269         if(this.panels.getCount() < 1){
56270             this.updateTitle("&#160;");
56271             this.closeBtn.hide();
56272             this.hide();
56273         }else{
56274             if(!this.isVisible()){
56275                 this.show();
56276             }
56277         }
56278     },
56279
56280     /**
56281      * Adds the passed ContentPanel(s) to this region.
56282      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56283      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56284      */
56285     add : function(panel){
56286         if(arguments.length > 1){
56287             for(var i = 0, len = arguments.length; i < len; i++) {
56288                 this.add(arguments[i]);
56289             }
56290             return null;
56291         }
56292         if(this.hasPanel(panel)){
56293             this.showPanel(panel);
56294             return panel;
56295         }
56296         panel.setRegion(this);
56297         this.panels.add(panel);
56298         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56299             this.bodyEl.dom.appendChild(panel.getEl().dom);
56300             if(panel.background !== true){
56301                 this.setActivePanel(panel);
56302             }
56303             this.fireEvent("paneladded", this, panel);
56304             return panel;
56305         }
56306         if(!this.tabs){
56307             this.initTabs();
56308         }else{
56309             this.initPanelAsTab(panel);
56310         }
56311         if(panel.background !== true){
56312             this.tabs.activate(panel.getEl().id);
56313         }
56314         this.fireEvent("paneladded", this, panel);
56315         return panel;
56316     },
56317
56318     /**
56319      * Hides the tab for the specified panel.
56320      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56321      */
56322     hidePanel : function(panel){
56323         if(this.tabs && (panel = this.getPanel(panel))){
56324             this.tabs.hideTab(panel.getEl().id);
56325         }
56326     },
56327
56328     /**
56329      * Unhides the tab for a previously hidden panel.
56330      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56331      */
56332     unhidePanel : function(panel){
56333         if(this.tabs && (panel = this.getPanel(panel))){
56334             this.tabs.unhideTab(panel.getEl().id);
56335         }
56336     },
56337
56338     clearPanels : function(){
56339         while(this.panels.getCount() > 0){
56340              this.remove(this.panels.first());
56341         }
56342     },
56343
56344     /**
56345      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56346      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56347      * @param {Boolean} preservePanel Overrides the config preservePanel option
56348      * @return {Roo.ContentPanel} The panel that was removed
56349      */
56350     remove : function(panel, preservePanel){
56351         panel = this.getPanel(panel);
56352         if(!panel){
56353             return null;
56354         }
56355         var e = {};
56356         this.fireEvent("beforeremove", this, panel, e);
56357         if(e.cancel === true){
56358             return null;
56359         }
56360         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56361         var panelId = panel.getId();
56362         this.panels.removeKey(panelId);
56363         if(preservePanel){
56364             document.body.appendChild(panel.getEl().dom);
56365         }
56366         if(this.tabs){
56367             this.tabs.removeTab(panel.getEl().id);
56368         }else if (!preservePanel){
56369             this.bodyEl.dom.removeChild(panel.getEl().dom);
56370         }
56371         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56372             var p = this.panels.first();
56373             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56374             tempEl.appendChild(p.getEl().dom);
56375             this.bodyEl.update("");
56376             this.bodyEl.dom.appendChild(p.getEl().dom);
56377             tempEl = null;
56378             this.updateTitle(p.getTitle());
56379             this.tabs = null;
56380             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56381             this.setActivePanel(p);
56382         }
56383         panel.setRegion(null);
56384         if(this.activePanel == panel){
56385             this.activePanel = null;
56386         }
56387         if(this.config.autoDestroy !== false && preservePanel !== true){
56388             try{panel.destroy();}catch(e){}
56389         }
56390         this.fireEvent("panelremoved", this, panel);
56391         return panel;
56392     },
56393
56394     /**
56395      * Returns the TabPanel component used by this region
56396      * @return {Roo.TabPanel}
56397      */
56398     getTabs : function(){
56399         return this.tabs;
56400     },
56401
56402     createTool : function(parentEl, className){
56403         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56404             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56405         btn.addClassOnOver("x-layout-tools-button-over");
56406         return btn;
56407     }
56408 });/*
56409  * Based on:
56410  * Ext JS Library 1.1.1
56411  * Copyright(c) 2006-2007, Ext JS, LLC.
56412  *
56413  * Originally Released Under LGPL - original licence link has changed is not relivant.
56414  *
56415  * Fork - LGPL
56416  * <script type="text/javascript">
56417  */
56418  
56419
56420
56421 /**
56422  * @class Roo.SplitLayoutRegion
56423  * @extends Roo.LayoutRegion
56424  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56425  */
56426 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56427     this.cursor = cursor;
56428     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56429 };
56430
56431 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56432     splitTip : "Drag to resize.",
56433     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56434     useSplitTips : false,
56435
56436     applyConfig : function(config){
56437         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56438         if(config.split){
56439             if(!this.split){
56440                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56441                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56442                 /** The SplitBar for this region 
56443                 * @type Roo.SplitBar */
56444                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56445                 this.split.on("moved", this.onSplitMove, this);
56446                 this.split.useShim = config.useShim === true;
56447                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56448                 if(this.useSplitTips){
56449                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56450                 }
56451                 if(config.collapsible){
56452                     this.split.el.on("dblclick", this.collapse,  this);
56453                 }
56454             }
56455             if(typeof config.minSize != "undefined"){
56456                 this.split.minSize = config.minSize;
56457             }
56458             if(typeof config.maxSize != "undefined"){
56459                 this.split.maxSize = config.maxSize;
56460             }
56461             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56462                 this.hideSplitter();
56463             }
56464         }
56465     },
56466
56467     getHMaxSize : function(){
56468          var cmax = this.config.maxSize || 10000;
56469          var center = this.mgr.getRegion("center");
56470          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56471     },
56472
56473     getVMaxSize : function(){
56474          var cmax = this.config.maxSize || 10000;
56475          var center = this.mgr.getRegion("center");
56476          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56477     },
56478
56479     onSplitMove : function(split, newSize){
56480         this.fireEvent("resized", this, newSize);
56481     },
56482     
56483     /** 
56484      * Returns the {@link Roo.SplitBar} for this region.
56485      * @return {Roo.SplitBar}
56486      */
56487     getSplitBar : function(){
56488         return this.split;
56489     },
56490     
56491     hide : function(){
56492         this.hideSplitter();
56493         Roo.SplitLayoutRegion.superclass.hide.call(this);
56494     },
56495
56496     hideSplitter : function(){
56497         if(this.split){
56498             this.split.el.setLocation(-2000,-2000);
56499             this.split.el.hide();
56500         }
56501     },
56502
56503     show : function(){
56504         if(this.split){
56505             this.split.el.show();
56506         }
56507         Roo.SplitLayoutRegion.superclass.show.call(this);
56508     },
56509     
56510     beforeSlide: function(){
56511         if(Roo.isGecko){// firefox overflow auto bug workaround
56512             this.bodyEl.clip();
56513             if(this.tabs) {
56514                 this.tabs.bodyEl.clip();
56515             }
56516             if(this.activePanel){
56517                 this.activePanel.getEl().clip();
56518                 
56519                 if(this.activePanel.beforeSlide){
56520                     this.activePanel.beforeSlide();
56521                 }
56522             }
56523         }
56524     },
56525     
56526     afterSlide : function(){
56527         if(Roo.isGecko){// firefox overflow auto bug workaround
56528             this.bodyEl.unclip();
56529             if(this.tabs) {
56530                 this.tabs.bodyEl.unclip();
56531             }
56532             if(this.activePanel){
56533                 this.activePanel.getEl().unclip();
56534                 if(this.activePanel.afterSlide){
56535                     this.activePanel.afterSlide();
56536                 }
56537             }
56538         }
56539     },
56540
56541     initAutoHide : function(){
56542         if(this.autoHide !== false){
56543             if(!this.autoHideHd){
56544                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56545                 this.autoHideHd = {
56546                     "mouseout": function(e){
56547                         if(!e.within(this.el, true)){
56548                             st.delay(500);
56549                         }
56550                     },
56551                     "mouseover" : function(e){
56552                         st.cancel();
56553                     },
56554                     scope : this
56555                 };
56556             }
56557             this.el.on(this.autoHideHd);
56558         }
56559     },
56560
56561     clearAutoHide : function(){
56562         if(this.autoHide !== false){
56563             this.el.un("mouseout", this.autoHideHd.mouseout);
56564             this.el.un("mouseover", this.autoHideHd.mouseover);
56565         }
56566     },
56567
56568     clearMonitor : function(){
56569         Roo.get(document).un("click", this.slideInIf, this);
56570     },
56571
56572     // these names are backwards but not changed for compat
56573     slideOut : function(){
56574         if(this.isSlid || this.el.hasActiveFx()){
56575             return;
56576         }
56577         this.isSlid = true;
56578         if(this.collapseBtn){
56579             this.collapseBtn.hide();
56580         }
56581         this.closeBtnState = this.closeBtn.getStyle('display');
56582         this.closeBtn.hide();
56583         if(this.stickBtn){
56584             this.stickBtn.show();
56585         }
56586         this.el.show();
56587         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56588         this.beforeSlide();
56589         this.el.setStyle("z-index", 10001);
56590         this.el.slideIn(this.getSlideAnchor(), {
56591             callback: function(){
56592                 this.afterSlide();
56593                 this.initAutoHide();
56594                 Roo.get(document).on("click", this.slideInIf, this);
56595                 this.fireEvent("slideshow", this);
56596             },
56597             scope: this,
56598             block: true
56599         });
56600     },
56601
56602     afterSlideIn : function(){
56603         this.clearAutoHide();
56604         this.isSlid = false;
56605         this.clearMonitor();
56606         this.el.setStyle("z-index", "");
56607         if(this.collapseBtn){
56608             this.collapseBtn.show();
56609         }
56610         this.closeBtn.setStyle('display', this.closeBtnState);
56611         if(this.stickBtn){
56612             this.stickBtn.hide();
56613         }
56614         this.fireEvent("slidehide", this);
56615     },
56616
56617     slideIn : function(cb){
56618         if(!this.isSlid || this.el.hasActiveFx()){
56619             Roo.callback(cb);
56620             return;
56621         }
56622         this.isSlid = false;
56623         this.beforeSlide();
56624         this.el.slideOut(this.getSlideAnchor(), {
56625             callback: function(){
56626                 this.el.setLeftTop(-10000, -10000);
56627                 this.afterSlide();
56628                 this.afterSlideIn();
56629                 Roo.callback(cb);
56630             },
56631             scope: this,
56632             block: true
56633         });
56634     },
56635     
56636     slideInIf : function(e){
56637         if(!e.within(this.el)){
56638             this.slideIn();
56639         }
56640     },
56641
56642     animateCollapse : function(){
56643         this.beforeSlide();
56644         this.el.setStyle("z-index", 20000);
56645         var anchor = this.getSlideAnchor();
56646         this.el.slideOut(anchor, {
56647             callback : function(){
56648                 this.el.setStyle("z-index", "");
56649                 this.collapsedEl.slideIn(anchor, {duration:.3});
56650                 this.afterSlide();
56651                 this.el.setLocation(-10000,-10000);
56652                 this.el.hide();
56653                 this.fireEvent("collapsed", this);
56654             },
56655             scope: this,
56656             block: true
56657         });
56658     },
56659
56660     animateExpand : function(){
56661         this.beforeSlide();
56662         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56663         this.el.setStyle("z-index", 20000);
56664         this.collapsedEl.hide({
56665             duration:.1
56666         });
56667         this.el.slideIn(this.getSlideAnchor(), {
56668             callback : function(){
56669                 this.el.setStyle("z-index", "");
56670                 this.afterSlide();
56671                 if(this.split){
56672                     this.split.el.show();
56673                 }
56674                 this.fireEvent("invalidated", this);
56675                 this.fireEvent("expanded", this);
56676             },
56677             scope: this,
56678             block: true
56679         });
56680     },
56681
56682     anchors : {
56683         "west" : "left",
56684         "east" : "right",
56685         "north" : "top",
56686         "south" : "bottom"
56687     },
56688
56689     sanchors : {
56690         "west" : "l",
56691         "east" : "r",
56692         "north" : "t",
56693         "south" : "b"
56694     },
56695
56696     canchors : {
56697         "west" : "tl-tr",
56698         "east" : "tr-tl",
56699         "north" : "tl-bl",
56700         "south" : "bl-tl"
56701     },
56702
56703     getAnchor : function(){
56704         return this.anchors[this.position];
56705     },
56706
56707     getCollapseAnchor : function(){
56708         return this.canchors[this.position];
56709     },
56710
56711     getSlideAnchor : function(){
56712         return this.sanchors[this.position];
56713     },
56714
56715     getAlignAdj : function(){
56716         var cm = this.cmargins;
56717         switch(this.position){
56718             case "west":
56719                 return [0, 0];
56720             break;
56721             case "east":
56722                 return [0, 0];
56723             break;
56724             case "north":
56725                 return [0, 0];
56726             break;
56727             case "south":
56728                 return [0, 0];
56729             break;
56730         }
56731     },
56732
56733     getExpandAdj : function(){
56734         var c = this.collapsedEl, cm = this.cmargins;
56735         switch(this.position){
56736             case "west":
56737                 return [-(cm.right+c.getWidth()+cm.left), 0];
56738             break;
56739             case "east":
56740                 return [cm.right+c.getWidth()+cm.left, 0];
56741             break;
56742             case "north":
56743                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56744             break;
56745             case "south":
56746                 return [0, cm.top+cm.bottom+c.getHeight()];
56747             break;
56748         }
56749     }
56750 });/*
56751  * Based on:
56752  * Ext JS Library 1.1.1
56753  * Copyright(c) 2006-2007, Ext JS, LLC.
56754  *
56755  * Originally Released Under LGPL - original licence link has changed is not relivant.
56756  *
56757  * Fork - LGPL
56758  * <script type="text/javascript">
56759  */
56760 /*
56761  * These classes are private internal classes
56762  */
56763 Roo.CenterLayoutRegion = function(mgr, config){
56764     Roo.LayoutRegion.call(this, mgr, config, "center");
56765     this.visible = true;
56766     this.minWidth = config.minWidth || 20;
56767     this.minHeight = config.minHeight || 20;
56768 };
56769
56770 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56771     hide : function(){
56772         // center panel can't be hidden
56773     },
56774     
56775     show : function(){
56776         // center panel can't be hidden
56777     },
56778     
56779     getMinWidth: function(){
56780         return this.minWidth;
56781     },
56782     
56783     getMinHeight: function(){
56784         return this.minHeight;
56785     }
56786 });
56787
56788
56789 Roo.NorthLayoutRegion = function(mgr, config){
56790     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56791     if(this.split){
56792         this.split.placement = Roo.SplitBar.TOP;
56793         this.split.orientation = Roo.SplitBar.VERTICAL;
56794         this.split.el.addClass("x-layout-split-v");
56795     }
56796     var size = config.initialSize || config.height;
56797     if(typeof size != "undefined"){
56798         this.el.setHeight(size);
56799     }
56800 };
56801 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56802     orientation: Roo.SplitBar.VERTICAL,
56803     getBox : function(){
56804         if(this.collapsed){
56805             return this.collapsedEl.getBox();
56806         }
56807         var box = this.el.getBox();
56808         if(this.split){
56809             box.height += this.split.el.getHeight();
56810         }
56811         return box;
56812     },
56813     
56814     updateBox : function(box){
56815         if(this.split && !this.collapsed){
56816             box.height -= this.split.el.getHeight();
56817             this.split.el.setLeft(box.x);
56818             this.split.el.setTop(box.y+box.height);
56819             this.split.el.setWidth(box.width);
56820         }
56821         if(this.collapsed){
56822             this.updateBody(box.width, null);
56823         }
56824         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56825     }
56826 });
56827
56828 Roo.SouthLayoutRegion = function(mgr, config){
56829     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56830     if(this.split){
56831         this.split.placement = Roo.SplitBar.BOTTOM;
56832         this.split.orientation = Roo.SplitBar.VERTICAL;
56833         this.split.el.addClass("x-layout-split-v");
56834     }
56835     var size = config.initialSize || config.height;
56836     if(typeof size != "undefined"){
56837         this.el.setHeight(size);
56838     }
56839 };
56840 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56841     orientation: Roo.SplitBar.VERTICAL,
56842     getBox : function(){
56843         if(this.collapsed){
56844             return this.collapsedEl.getBox();
56845         }
56846         var box = this.el.getBox();
56847         if(this.split){
56848             var sh = this.split.el.getHeight();
56849             box.height += sh;
56850             box.y -= sh;
56851         }
56852         return box;
56853     },
56854     
56855     updateBox : function(box){
56856         if(this.split && !this.collapsed){
56857             var sh = this.split.el.getHeight();
56858             box.height -= sh;
56859             box.y += sh;
56860             this.split.el.setLeft(box.x);
56861             this.split.el.setTop(box.y-sh);
56862             this.split.el.setWidth(box.width);
56863         }
56864         if(this.collapsed){
56865             this.updateBody(box.width, null);
56866         }
56867         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56868     }
56869 });
56870
56871 Roo.EastLayoutRegion = function(mgr, config){
56872     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56873     if(this.split){
56874         this.split.placement = Roo.SplitBar.RIGHT;
56875         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56876         this.split.el.addClass("x-layout-split-h");
56877     }
56878     var size = config.initialSize || config.width;
56879     if(typeof size != "undefined"){
56880         this.el.setWidth(size);
56881     }
56882 };
56883 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56884     orientation: Roo.SplitBar.HORIZONTAL,
56885     getBox : function(){
56886         if(this.collapsed){
56887             return this.collapsedEl.getBox();
56888         }
56889         var box = this.el.getBox();
56890         if(this.split){
56891             var sw = this.split.el.getWidth();
56892             box.width += sw;
56893             box.x -= sw;
56894         }
56895         return box;
56896     },
56897
56898     updateBox : function(box){
56899         if(this.split && !this.collapsed){
56900             var sw = this.split.el.getWidth();
56901             box.width -= sw;
56902             this.split.el.setLeft(box.x);
56903             this.split.el.setTop(box.y);
56904             this.split.el.setHeight(box.height);
56905             box.x += sw;
56906         }
56907         if(this.collapsed){
56908             this.updateBody(null, box.height);
56909         }
56910         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56911     }
56912 });
56913
56914 Roo.WestLayoutRegion = function(mgr, config){
56915     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56916     if(this.split){
56917         this.split.placement = Roo.SplitBar.LEFT;
56918         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56919         this.split.el.addClass("x-layout-split-h");
56920     }
56921     var size = config.initialSize || config.width;
56922     if(typeof size != "undefined"){
56923         this.el.setWidth(size);
56924     }
56925 };
56926 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56927     orientation: Roo.SplitBar.HORIZONTAL,
56928     getBox : function(){
56929         if(this.collapsed){
56930             return this.collapsedEl.getBox();
56931         }
56932         var box = this.el.getBox();
56933         if(this.split){
56934             box.width += this.split.el.getWidth();
56935         }
56936         return box;
56937     },
56938     
56939     updateBox : function(box){
56940         if(this.split && !this.collapsed){
56941             var sw = this.split.el.getWidth();
56942             box.width -= sw;
56943             this.split.el.setLeft(box.x+box.width);
56944             this.split.el.setTop(box.y);
56945             this.split.el.setHeight(box.height);
56946         }
56947         if(this.collapsed){
56948             this.updateBody(null, box.height);
56949         }
56950         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56951     }
56952 });
56953 /*
56954  * Based on:
56955  * Ext JS Library 1.1.1
56956  * Copyright(c) 2006-2007, Ext JS, LLC.
56957  *
56958  * Originally Released Under LGPL - original licence link has changed is not relivant.
56959  *
56960  * Fork - LGPL
56961  * <script type="text/javascript">
56962  */
56963  
56964  
56965 /*
56966  * Private internal class for reading and applying state
56967  */
56968 Roo.LayoutStateManager = function(layout){
56969      // default empty state
56970      this.state = {
56971         north: {},
56972         south: {},
56973         east: {},
56974         west: {}       
56975     };
56976 };
56977
56978 Roo.LayoutStateManager.prototype = {
56979     init : function(layout, provider){
56980         this.provider = provider;
56981         var state = provider.get(layout.id+"-layout-state");
56982         if(state){
56983             var wasUpdating = layout.isUpdating();
56984             if(!wasUpdating){
56985                 layout.beginUpdate();
56986             }
56987             for(var key in state){
56988                 if(typeof state[key] != "function"){
56989                     var rstate = state[key];
56990                     var r = layout.getRegion(key);
56991                     if(r && rstate){
56992                         if(rstate.size){
56993                             r.resizeTo(rstate.size);
56994                         }
56995                         if(rstate.collapsed == true){
56996                             r.collapse(true);
56997                         }else{
56998                             r.expand(null, true);
56999                         }
57000                     }
57001                 }
57002             }
57003             if(!wasUpdating){
57004                 layout.endUpdate();
57005             }
57006             this.state = state; 
57007         }
57008         this.layout = layout;
57009         layout.on("regionresized", this.onRegionResized, this);
57010         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57011         layout.on("regionexpanded", this.onRegionExpanded, this);
57012     },
57013     
57014     storeState : function(){
57015         this.provider.set(this.layout.id+"-layout-state", this.state);
57016     },
57017     
57018     onRegionResized : function(region, newSize){
57019         this.state[region.getPosition()].size = newSize;
57020         this.storeState();
57021     },
57022     
57023     onRegionCollapsed : function(region){
57024         this.state[region.getPosition()].collapsed = true;
57025         this.storeState();
57026     },
57027     
57028     onRegionExpanded : function(region){
57029         this.state[region.getPosition()].collapsed = false;
57030         this.storeState();
57031     }
57032 };/*
57033  * Based on:
57034  * Ext JS Library 1.1.1
57035  * Copyright(c) 2006-2007, Ext JS, LLC.
57036  *
57037  * Originally Released Under LGPL - original licence link has changed is not relivant.
57038  *
57039  * Fork - LGPL
57040  * <script type="text/javascript">
57041  */
57042 /**
57043  * @class Roo.ContentPanel
57044  * @extends Roo.util.Observable
57045  * @children Roo.form.Form Roo.JsonView Roo.View
57046  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57047  * A basic ContentPanel element.
57048  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57049  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57050  * @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
57051  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57052  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57053  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57054  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57055  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57056  * @cfg {String} title          The title for this panel
57057  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57058  * @cfg {String} url            Calls {@link #setUrl} with this value
57059  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57060  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57061  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57062  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57063  * @cfg {String}    style  Extra style to add to the content panel
57064  * @cfg {Roo.menu.Menu} menu  popup menu
57065
57066  * @constructor
57067  * Create a new ContentPanel.
57068  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57069  * @param {String/Object} config A string to set only the title or a config object
57070  * @param {String} content (optional) Set the HTML content for this panel
57071  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57072  */
57073 Roo.ContentPanel = function(el, config, content){
57074     
57075      
57076     /*
57077     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57078         config = el;
57079         el = Roo.id();
57080     }
57081     if (config && config.parentLayout) { 
57082         el = config.parentLayout.el.createChild(); 
57083     }
57084     */
57085     if(el.autoCreate){ // xtype is available if this is called from factory
57086         config = el;
57087         el = Roo.id();
57088     }
57089     this.el = Roo.get(el);
57090     if(!this.el && config && config.autoCreate){
57091         if(typeof config.autoCreate == "object"){
57092             if(!config.autoCreate.id){
57093                 config.autoCreate.id = config.id||el;
57094             }
57095             this.el = Roo.DomHelper.append(document.body,
57096                         config.autoCreate, true);
57097         }else{
57098             this.el = Roo.DomHelper.append(document.body,
57099                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57100         }
57101     }
57102     
57103     
57104     this.closable = false;
57105     this.loaded = false;
57106     this.active = false;
57107     if(typeof config == "string"){
57108         this.title = config;
57109     }else{
57110         Roo.apply(this, config);
57111     }
57112     
57113     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57114         this.wrapEl = this.el.wrap();
57115         this.toolbar.container = this.el.insertSibling(false, 'before');
57116         this.toolbar = new Roo.Toolbar(this.toolbar);
57117     }
57118     
57119     // xtype created footer. - not sure if will work as we normally have to render first..
57120     if (this.footer && !this.footer.el && this.footer.xtype) {
57121         if (!this.wrapEl) {
57122             this.wrapEl = this.el.wrap();
57123         }
57124     
57125         this.footer.container = this.wrapEl.createChild();
57126          
57127         this.footer = Roo.factory(this.footer, Roo);
57128         
57129     }
57130     
57131     if(this.resizeEl){
57132         this.resizeEl = Roo.get(this.resizeEl, true);
57133     }else{
57134         this.resizeEl = this.el;
57135     }
57136     // handle view.xtype
57137     
57138  
57139     
57140     
57141     this.addEvents({
57142         /**
57143          * @event activate
57144          * Fires when this panel is activated. 
57145          * @param {Roo.ContentPanel} this
57146          */
57147         "activate" : true,
57148         /**
57149          * @event deactivate
57150          * Fires when this panel is activated. 
57151          * @param {Roo.ContentPanel} this
57152          */
57153         "deactivate" : true,
57154
57155         /**
57156          * @event resize
57157          * Fires when this panel is resized if fitToFrame is true.
57158          * @param {Roo.ContentPanel} this
57159          * @param {Number} width The width after any component adjustments
57160          * @param {Number} height The height after any component adjustments
57161          */
57162         "resize" : true,
57163         
57164          /**
57165          * @event render
57166          * Fires when this tab is created
57167          * @param {Roo.ContentPanel} this
57168          */
57169         "render" : true
57170          
57171         
57172     });
57173     
57174
57175     
57176     
57177     if(this.autoScroll){
57178         this.resizeEl.setStyle("overflow", "auto");
57179     } else {
57180         // fix randome scrolling
57181         this.el.on('scroll', function() {
57182             Roo.log('fix random scolling');
57183             this.scrollTo('top',0); 
57184         });
57185     }
57186     content = content || this.content;
57187     if(content){
57188         this.setContent(content);
57189     }
57190     if(config && config.url){
57191         this.setUrl(this.url, this.params, this.loadOnce);
57192     }
57193     
57194     
57195     
57196     Roo.ContentPanel.superclass.constructor.call(this);
57197     
57198     if (this.view && typeof(this.view.xtype) != 'undefined') {
57199         this.view.el = this.el.appendChild(document.createElement("div"));
57200         this.view = Roo.factory(this.view); 
57201         this.view.render  &&  this.view.render(false, '');  
57202     }
57203     
57204     
57205     this.fireEvent('render', this);
57206 };
57207
57208 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57209     tabTip:'',
57210     setRegion : function(region){
57211         this.region = region;
57212         if(region){
57213            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57214         }else{
57215            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57216         } 
57217     },
57218     
57219     /**
57220      * Returns the toolbar for this Panel if one was configured. 
57221      * @return {Roo.Toolbar} 
57222      */
57223     getToolbar : function(){
57224         return this.toolbar;
57225     },
57226     
57227     setActiveState : function(active){
57228         this.active = active;
57229         if(!active){
57230             this.fireEvent("deactivate", this);
57231         }else{
57232             this.fireEvent("activate", this);
57233         }
57234     },
57235     /**
57236      * Updates this panel's element
57237      * @param {String} content The new content
57238      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57239     */
57240     setContent : function(content, loadScripts){
57241         this.el.update(content, loadScripts);
57242     },
57243
57244     ignoreResize : function(w, h){
57245         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57246             return true;
57247         }else{
57248             this.lastSize = {width: w, height: h};
57249             return false;
57250         }
57251     },
57252     /**
57253      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57254      * @return {Roo.UpdateManager} The UpdateManager
57255      */
57256     getUpdateManager : function(){
57257         return this.el.getUpdateManager();
57258     },
57259      /**
57260      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57261      * @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:
57262 <pre><code>
57263 panel.load({
57264     url: "your-url.php",
57265     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57266     callback: yourFunction,
57267     scope: yourObject, //(optional scope)
57268     discardUrl: false,
57269     nocache: false,
57270     text: "Loading...",
57271     timeout: 30,
57272     scripts: false
57273 });
57274 </code></pre>
57275      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57276      * 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.
57277      * @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}
57278      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57279      * @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.
57280      * @return {Roo.ContentPanel} this
57281      */
57282     load : function(){
57283         var um = this.el.getUpdateManager();
57284         um.update.apply(um, arguments);
57285         return this;
57286     },
57287
57288
57289     /**
57290      * 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.
57291      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57292      * @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)
57293      * @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)
57294      * @return {Roo.UpdateManager} The UpdateManager
57295      */
57296     setUrl : function(url, params, loadOnce){
57297         if(this.refreshDelegate){
57298             this.removeListener("activate", this.refreshDelegate);
57299         }
57300         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57301         this.on("activate", this.refreshDelegate);
57302         return this.el.getUpdateManager();
57303     },
57304     
57305     _handleRefresh : function(url, params, loadOnce){
57306         if(!loadOnce || !this.loaded){
57307             var updater = this.el.getUpdateManager();
57308             updater.update(url, params, this._setLoaded.createDelegate(this));
57309         }
57310     },
57311     
57312     _setLoaded : function(){
57313         this.loaded = true;
57314     }, 
57315     
57316     /**
57317      * Returns this panel's id
57318      * @return {String} 
57319      */
57320     getId : function(){
57321         return this.el.id;
57322     },
57323     
57324     /** 
57325      * Returns this panel's element - used by regiosn to add.
57326      * @return {Roo.Element} 
57327      */
57328     getEl : function(){
57329         return this.wrapEl || this.el;
57330     },
57331     
57332     adjustForComponents : function(width, height)
57333     {
57334         //Roo.log('adjustForComponents ');
57335         if(this.resizeEl != this.el){
57336             width -= this.el.getFrameWidth('lr');
57337             height -= this.el.getFrameWidth('tb');
57338         }
57339         if(this.toolbar){
57340             var te = this.toolbar.getEl();
57341             height -= te.getHeight();
57342             te.setWidth(width);
57343         }
57344         if(this.footer){
57345             var te = this.footer.getEl();
57346             //Roo.log("footer:" + te.getHeight());
57347             
57348             height -= te.getHeight();
57349             te.setWidth(width);
57350         }
57351         
57352         
57353         if(this.adjustments){
57354             width += this.adjustments[0];
57355             height += this.adjustments[1];
57356         }
57357         return {"width": width, "height": height};
57358     },
57359     
57360     setSize : function(width, height){
57361         if(this.fitToFrame && !this.ignoreResize(width, height)){
57362             if(this.fitContainer && this.resizeEl != this.el){
57363                 this.el.setSize(width, height);
57364             }
57365             var size = this.adjustForComponents(width, height);
57366             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57367             this.fireEvent('resize', this, size.width, size.height);
57368         }
57369     },
57370     
57371     /**
57372      * Returns this panel's title
57373      * @return {String} 
57374      */
57375     getTitle : function(){
57376         return this.title;
57377     },
57378     
57379     /**
57380      * Set this panel's title
57381      * @param {String} title
57382      */
57383     setTitle : function(title){
57384         this.title = title;
57385         if(this.region){
57386             this.region.updatePanelTitle(this, title);
57387         }
57388     },
57389     
57390     /**
57391      * Returns true is this panel was configured to be closable
57392      * @return {Boolean} 
57393      */
57394     isClosable : function(){
57395         return this.closable;
57396     },
57397     
57398     beforeSlide : function(){
57399         this.el.clip();
57400         this.resizeEl.clip();
57401     },
57402     
57403     afterSlide : function(){
57404         this.el.unclip();
57405         this.resizeEl.unclip();
57406     },
57407     
57408     /**
57409      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57410      *   Will fail silently if the {@link #setUrl} method has not been called.
57411      *   This does not activate the panel, just updates its content.
57412      */
57413     refresh : function(){
57414         if(this.refreshDelegate){
57415            this.loaded = false;
57416            this.refreshDelegate();
57417         }
57418     },
57419     
57420     /**
57421      * Destroys this panel
57422      */
57423     destroy : function(){
57424         this.el.removeAllListeners();
57425         var tempEl = document.createElement("span");
57426         tempEl.appendChild(this.el.dom);
57427         tempEl.innerHTML = "";
57428         this.el.remove();
57429         this.el = null;
57430     },
57431     
57432     /**
57433      * form - if the content panel contains a form - this is a reference to it.
57434      * @type {Roo.form.Form}
57435      */
57436     form : false,
57437     /**
57438      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57439      *    This contains a reference to it.
57440      * @type {Roo.View}
57441      */
57442     view : false,
57443     
57444       /**
57445      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57446      * <pre><code>
57447
57448 layout.addxtype({
57449        xtype : 'Form',
57450        items: [ .... ]
57451    }
57452 );
57453
57454 </code></pre>
57455      * @param {Object} cfg Xtype definition of item to add.
57456      */
57457     
57458     addxtype : function(cfg) {
57459         // add form..
57460         if (cfg.xtype.match(/^Form$/)) {
57461             
57462             var el;
57463             //if (this.footer) {
57464             //    el = this.footer.container.insertSibling(false, 'before');
57465             //} else {
57466                 el = this.el.createChild();
57467             //}
57468
57469             this.form = new  Roo.form.Form(cfg);
57470             
57471             
57472             if ( this.form.allItems.length) {
57473                 this.form.render(el.dom);
57474             }
57475             return this.form;
57476         }
57477         // should only have one of theses..
57478         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57479             // views.. should not be just added - used named prop 'view''
57480             
57481             cfg.el = this.el.appendChild(document.createElement("div"));
57482             // factory?
57483             
57484             var ret = new Roo.factory(cfg);
57485              
57486              ret.render && ret.render(false, ''); // render blank..
57487             this.view = ret;
57488             return ret;
57489         }
57490         return false;
57491     }
57492 });
57493
57494 /**
57495  * @class Roo.GridPanel
57496  * @extends Roo.ContentPanel
57497  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57498  * @constructor
57499  * Create a new GridPanel.
57500  * @cfg {Roo.grid.Grid} grid The grid for this panel
57501  */
57502 Roo.GridPanel = function(grid, config){
57503     
57504     // universal ctor...
57505     if (typeof(grid.grid) != 'undefined') {
57506         config = grid;
57507         grid = config.grid;
57508     }
57509     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57510         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57511         
57512     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57513     
57514     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57515     
57516     if(this.toolbar){
57517         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57518     }
57519     // xtype created footer. - not sure if will work as we normally have to render first..
57520     if (this.footer && !this.footer.el && this.footer.xtype) {
57521         
57522         this.footer.container = this.grid.getView().getFooterPanel(true);
57523         this.footer.dataSource = this.grid.dataSource;
57524         this.footer = Roo.factory(this.footer, Roo);
57525         
57526     }
57527     
57528     grid.monitorWindowResize = false; // turn off autosizing
57529     grid.autoHeight = false;
57530     grid.autoWidth = false;
57531     this.grid = grid;
57532     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57533 };
57534
57535 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57536     getId : function(){
57537         return this.grid.id;
57538     },
57539     
57540     /**
57541      * Returns the grid for this panel
57542      * @return {Roo.grid.Grid} 
57543      */
57544     getGrid : function(){
57545         return this.grid;    
57546     },
57547     
57548     setSize : function(width, height){
57549         if(!this.ignoreResize(width, height)){
57550             var grid = this.grid;
57551             var size = this.adjustForComponents(width, height);
57552             grid.getGridEl().setSize(size.width, size.height);
57553             grid.autoSize();
57554         }
57555     },
57556     
57557     beforeSlide : function(){
57558         this.grid.getView().scroller.clip();
57559     },
57560     
57561     afterSlide : function(){
57562         this.grid.getView().scroller.unclip();
57563     },
57564     
57565     destroy : function(){
57566         this.grid.destroy();
57567         delete this.grid;
57568         Roo.GridPanel.superclass.destroy.call(this); 
57569     }
57570 });
57571
57572
57573 /**
57574  * @class Roo.NestedLayoutPanel
57575  * @extends Roo.ContentPanel
57576  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57577  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57578  *
57579  * 
57580  * @constructor
57581  * Create a new NestedLayoutPanel.
57582  * 
57583  * 
57584  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57585  * @param {String/Object} config A string to set only the title or a config object
57586  */
57587 Roo.NestedLayoutPanel = function(layout, config)
57588 {
57589     // construct with only one argument..
57590     /* FIXME - implement nicer consturctors
57591     if (layout.layout) {
57592         config = layout;
57593         layout = config.layout;
57594         delete config.layout;
57595     }
57596     if (layout.xtype && !layout.getEl) {
57597         // then layout needs constructing..
57598         layout = Roo.factory(layout, Roo);
57599     }
57600     */
57601     
57602     
57603     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57604     
57605     layout.monitorWindowResize = false; // turn off autosizing
57606     this.layout = layout;
57607     this.layout.getEl().addClass("x-layout-nested-layout");
57608     
57609     
57610     
57611     
57612 };
57613
57614 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57615
57616     setSize : function(width, height){
57617         if(!this.ignoreResize(width, height)){
57618             var size = this.adjustForComponents(width, height);
57619             var el = this.layout.getEl();
57620             el.setSize(size.width, size.height);
57621             var touch = el.dom.offsetWidth;
57622             this.layout.layout();
57623             // ie requires a double layout on the first pass
57624             if(Roo.isIE && !this.initialized){
57625                 this.initialized = true;
57626                 this.layout.layout();
57627             }
57628         }
57629     },
57630     
57631     // activate all subpanels if not currently active..
57632     
57633     setActiveState : function(active){
57634         this.active = active;
57635         if(!active){
57636             this.fireEvent("deactivate", this);
57637             return;
57638         }
57639         
57640         this.fireEvent("activate", this);
57641         // not sure if this should happen before or after..
57642         if (!this.layout) {
57643             return; // should not happen..
57644         }
57645         var reg = false;
57646         for (var r in this.layout.regions) {
57647             reg = this.layout.getRegion(r);
57648             if (reg.getActivePanel()) {
57649                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57650                 reg.setActivePanel(reg.getActivePanel());
57651                 continue;
57652             }
57653             if (!reg.panels.length) {
57654                 continue;
57655             }
57656             reg.showPanel(reg.getPanel(0));
57657         }
57658         
57659         
57660         
57661         
57662     },
57663     
57664     /**
57665      * Returns the nested BorderLayout for this panel
57666      * @return {Roo.BorderLayout} 
57667      */
57668     getLayout : function(){
57669         return this.layout;
57670     },
57671     
57672      /**
57673      * Adds a xtype elements to the layout of the nested panel
57674      * <pre><code>
57675
57676 panel.addxtype({
57677        xtype : 'ContentPanel',
57678        region: 'west',
57679        items: [ .... ]
57680    }
57681 );
57682
57683 panel.addxtype({
57684         xtype : 'NestedLayoutPanel',
57685         region: 'west',
57686         layout: {
57687            center: { },
57688            west: { }   
57689         },
57690         items : [ ... list of content panels or nested layout panels.. ]
57691    }
57692 );
57693 </code></pre>
57694      * @param {Object} cfg Xtype definition of item to add.
57695      */
57696     addxtype : function(cfg) {
57697         return this.layout.addxtype(cfg);
57698     
57699     }
57700 });
57701
57702 Roo.ScrollPanel = function(el, config, content){
57703     config = config || {};
57704     config.fitToFrame = true;
57705     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57706     
57707     this.el.dom.style.overflow = "hidden";
57708     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57709     this.el.removeClass("x-layout-inactive-content");
57710     this.el.on("mousewheel", this.onWheel, this);
57711
57712     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57713     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57714     up.unselectable(); down.unselectable();
57715     up.on("click", this.scrollUp, this);
57716     down.on("click", this.scrollDown, this);
57717     up.addClassOnOver("x-scroller-btn-over");
57718     down.addClassOnOver("x-scroller-btn-over");
57719     up.addClassOnClick("x-scroller-btn-click");
57720     down.addClassOnClick("x-scroller-btn-click");
57721     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57722
57723     this.resizeEl = this.el;
57724     this.el = wrap; this.up = up; this.down = down;
57725 };
57726
57727 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57728     increment : 100,
57729     wheelIncrement : 5,
57730     scrollUp : function(){
57731         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57732     },
57733
57734     scrollDown : function(){
57735         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57736     },
57737
57738     afterScroll : function(){
57739         var el = this.resizeEl;
57740         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57741         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57742         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57743     },
57744
57745     setSize : function(){
57746         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57747         this.afterScroll();
57748     },
57749
57750     onWheel : function(e){
57751         var d = e.getWheelDelta();
57752         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57753         this.afterScroll();
57754         e.stopEvent();
57755     },
57756
57757     setContent : function(content, loadScripts){
57758         this.resizeEl.update(content, loadScripts);
57759     }
57760
57761 });
57762
57763
57764
57765 /**
57766  * @class Roo.TreePanel
57767  * @extends Roo.ContentPanel
57768  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57769  * Treepanel component
57770  * 
57771  * @constructor
57772  * Create a new TreePanel. - defaults to fit/scoll contents.
57773  * @param {String/Object} config A string to set only the panel's title, or a config object
57774  */
57775 Roo.TreePanel = function(config){
57776     var el = config.el;
57777     var tree = config.tree;
57778     delete config.tree; 
57779     delete config.el; // hopefull!
57780     
57781     // wrapper for IE7 strict & safari scroll issue
57782     
57783     var treeEl = el.createChild();
57784     config.resizeEl = treeEl;
57785     
57786     
57787     
57788     Roo.TreePanel.superclass.constructor.call(this, el, config);
57789  
57790  
57791     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57792     //console.log(tree);
57793     this.on('activate', function()
57794     {
57795         if (this.tree.rendered) {
57796             return;
57797         }
57798         //console.log('render tree');
57799         this.tree.render();
57800     });
57801     // this should not be needed.. - it's actually the 'el' that resizes?
57802     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57803     
57804     //this.on('resize',  function (cp, w, h) {
57805     //        this.tree.innerCt.setWidth(w);
57806     //        this.tree.innerCt.setHeight(h);
57807     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57808     //});
57809
57810         
57811     
57812 };
57813
57814 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57815     fitToFrame : true,
57816     autoScroll : true,
57817     /*
57818      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57819      */
57820     tree : false
57821
57822 });
57823
57824
57825
57826
57827
57828
57829
57830
57831
57832
57833
57834 /*
57835  * Based on:
57836  * Ext JS Library 1.1.1
57837  * Copyright(c) 2006-2007, Ext JS, LLC.
57838  *
57839  * Originally Released Under LGPL - original licence link has changed is not relivant.
57840  *
57841  * Fork - LGPL
57842  * <script type="text/javascript">
57843  */
57844  
57845
57846 /**
57847  * @class Roo.ReaderLayout
57848  * @extends Roo.BorderLayout
57849  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57850  * center region containing two nested regions (a top one for a list view and one for item preview below),
57851  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57852  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57853  * expedites the setup of the overall layout and regions for this common application style.
57854  * Example:
57855  <pre><code>
57856 var reader = new Roo.ReaderLayout();
57857 var CP = Roo.ContentPanel;  // shortcut for adding
57858
57859 reader.beginUpdate();
57860 reader.add("north", new CP("north", "North"));
57861 reader.add("west", new CP("west", {title: "West"}));
57862 reader.add("east", new CP("east", {title: "East"}));
57863
57864 reader.regions.listView.add(new CP("listView", "List"));
57865 reader.regions.preview.add(new CP("preview", "Preview"));
57866 reader.endUpdate();
57867 </code></pre>
57868 * @constructor
57869 * Create a new ReaderLayout
57870 * @param {Object} config Configuration options
57871 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57872 * document.body if omitted)
57873 */
57874 Roo.ReaderLayout = function(config, renderTo){
57875     var c = config || {size:{}};
57876     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57877         north: c.north !== false ? Roo.apply({
57878             split:false,
57879             initialSize: 32,
57880             titlebar: false
57881         }, c.north) : false,
57882         west: c.west !== false ? Roo.apply({
57883             split:true,
57884             initialSize: 200,
57885             minSize: 175,
57886             maxSize: 400,
57887             titlebar: true,
57888             collapsible: true,
57889             animate: true,
57890             margins:{left:5,right:0,bottom:5,top:5},
57891             cmargins:{left:5,right:5,bottom:5,top:5}
57892         }, c.west) : false,
57893         east: c.east !== false ? Roo.apply({
57894             split:true,
57895             initialSize: 200,
57896             minSize: 175,
57897             maxSize: 400,
57898             titlebar: true,
57899             collapsible: true,
57900             animate: true,
57901             margins:{left:0,right:5,bottom:5,top:5},
57902             cmargins:{left:5,right:5,bottom:5,top:5}
57903         }, c.east) : false,
57904         center: Roo.apply({
57905             tabPosition: 'top',
57906             autoScroll:false,
57907             closeOnTab: true,
57908             titlebar:false,
57909             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57910         }, c.center)
57911     });
57912
57913     this.el.addClass('x-reader');
57914
57915     this.beginUpdate();
57916
57917     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57918         south: c.preview !== false ? Roo.apply({
57919             split:true,
57920             initialSize: 200,
57921             minSize: 100,
57922             autoScroll:true,
57923             collapsible:true,
57924             titlebar: true,
57925             cmargins:{top:5,left:0, right:0, bottom:0}
57926         }, c.preview) : false,
57927         center: Roo.apply({
57928             autoScroll:false,
57929             titlebar:false,
57930             minHeight:200
57931         }, c.listView)
57932     });
57933     this.add('center', new Roo.NestedLayoutPanel(inner,
57934             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57935
57936     this.endUpdate();
57937
57938     this.regions.preview = inner.getRegion('south');
57939     this.regions.listView = inner.getRegion('center');
57940 };
57941
57942 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57943  * Based on:
57944  * Ext JS Library 1.1.1
57945  * Copyright(c) 2006-2007, Ext JS, LLC.
57946  *
57947  * Originally Released Under LGPL - original licence link has changed is not relivant.
57948  *
57949  * Fork - LGPL
57950  * <script type="text/javascript">
57951  */
57952  
57953 /**
57954  * @class Roo.grid.Grid
57955  * @extends Roo.util.Observable
57956  * This class represents the primary interface of a component based grid control.
57957  * <br><br>Usage:<pre><code>
57958  var grid = new Roo.grid.Grid("my-container-id", {
57959      ds: myDataStore,
57960      cm: myColModel,
57961      selModel: mySelectionModel,
57962      autoSizeColumns: true,
57963      monitorWindowResize: false,
57964      trackMouseOver: true
57965  });
57966  // set any options
57967  grid.render();
57968  * </code></pre>
57969  * <b>Common Problems:</b><br/>
57970  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57971  * element will correct this<br/>
57972  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57973  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57974  * are unpredictable.<br/>
57975  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57976  * grid to calculate dimensions/offsets.<br/>
57977   * @constructor
57978  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57979  * The container MUST have some type of size defined for the grid to fill. The container will be
57980  * automatically set to position relative if it isn't already.
57981  * @param {Object} config A config object that sets properties on this grid.
57982  */
57983 Roo.grid.Grid = function(container, config){
57984         // initialize the container
57985         this.container = Roo.get(container);
57986         this.container.update("");
57987         this.container.setStyle("overflow", "hidden");
57988     this.container.addClass('x-grid-container');
57989
57990     this.id = this.container.id;
57991
57992     Roo.apply(this, config);
57993     // check and correct shorthanded configs
57994     if(this.ds){
57995         this.dataSource = this.ds;
57996         delete this.ds;
57997     }
57998     if(this.cm){
57999         this.colModel = this.cm;
58000         delete this.cm;
58001     }
58002     if(this.sm){
58003         this.selModel = this.sm;
58004         delete this.sm;
58005     }
58006
58007     if (this.selModel) {
58008         this.selModel = Roo.factory(this.selModel, Roo.grid);
58009         this.sm = this.selModel;
58010         this.sm.xmodule = this.xmodule || false;
58011     }
58012     if (typeof(this.colModel.config) == 'undefined') {
58013         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58014         this.cm = this.colModel;
58015         this.cm.xmodule = this.xmodule || false;
58016     }
58017     if (this.dataSource) {
58018         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58019         this.ds = this.dataSource;
58020         this.ds.xmodule = this.xmodule || false;
58021          
58022     }
58023     
58024     
58025     
58026     if(this.width){
58027         this.container.setWidth(this.width);
58028     }
58029
58030     if(this.height){
58031         this.container.setHeight(this.height);
58032     }
58033     /** @private */
58034         this.addEvents({
58035         // raw events
58036         /**
58037          * @event click
58038          * The raw click event for the entire grid.
58039          * @param {Roo.EventObject} e
58040          */
58041         "click" : true,
58042         /**
58043          * @event dblclick
58044          * The raw dblclick event for the entire grid.
58045          * @param {Roo.EventObject} e
58046          */
58047         "dblclick" : true,
58048         /**
58049          * @event contextmenu
58050          * The raw contextmenu event for the entire grid.
58051          * @param {Roo.EventObject} e
58052          */
58053         "contextmenu" : true,
58054         /**
58055          * @event mousedown
58056          * The raw mousedown event for the entire grid.
58057          * @param {Roo.EventObject} e
58058          */
58059         "mousedown" : true,
58060         /**
58061          * @event mouseup
58062          * The raw mouseup event for the entire grid.
58063          * @param {Roo.EventObject} e
58064          */
58065         "mouseup" : true,
58066         /**
58067          * @event mouseover
58068          * The raw mouseover event for the entire grid.
58069          * @param {Roo.EventObject} e
58070          */
58071         "mouseover" : true,
58072         /**
58073          * @event mouseout
58074          * The raw mouseout event for the entire grid.
58075          * @param {Roo.EventObject} e
58076          */
58077         "mouseout" : true,
58078         /**
58079          * @event keypress
58080          * The raw keypress event for the entire grid.
58081          * @param {Roo.EventObject} e
58082          */
58083         "keypress" : true,
58084         /**
58085          * @event keydown
58086          * The raw keydown event for the entire grid.
58087          * @param {Roo.EventObject} e
58088          */
58089         "keydown" : true,
58090
58091         // custom events
58092
58093         /**
58094          * @event cellclick
58095          * Fires when a cell is clicked
58096          * @param {Grid} this
58097          * @param {Number} rowIndex
58098          * @param {Number} columnIndex
58099          * @param {Roo.EventObject} e
58100          */
58101         "cellclick" : true,
58102         /**
58103          * @event celldblclick
58104          * Fires when a cell is double clicked
58105          * @param {Grid} this
58106          * @param {Number} rowIndex
58107          * @param {Number} columnIndex
58108          * @param {Roo.EventObject} e
58109          */
58110         "celldblclick" : true,
58111         /**
58112          * @event rowclick
58113          * Fires when a row is clicked
58114          * @param {Grid} this
58115          * @param {Number} rowIndex
58116          * @param {Roo.EventObject} e
58117          */
58118         "rowclick" : true,
58119         /**
58120          * @event rowdblclick
58121          * Fires when a row is double clicked
58122          * @param {Grid} this
58123          * @param {Number} rowIndex
58124          * @param {Roo.EventObject} e
58125          */
58126         "rowdblclick" : true,
58127         /**
58128          * @event headerclick
58129          * Fires when a header is clicked
58130          * @param {Grid} this
58131          * @param {Number} columnIndex
58132          * @param {Roo.EventObject} e
58133          */
58134         "headerclick" : true,
58135         /**
58136          * @event headerdblclick
58137          * Fires when a header cell is double clicked
58138          * @param {Grid} this
58139          * @param {Number} columnIndex
58140          * @param {Roo.EventObject} e
58141          */
58142         "headerdblclick" : true,
58143         /**
58144          * @event rowcontextmenu
58145          * Fires when a row is right clicked
58146          * @param {Grid} this
58147          * @param {Number} rowIndex
58148          * @param {Roo.EventObject} e
58149          */
58150         "rowcontextmenu" : true,
58151         /**
58152          * @event cellcontextmenu
58153          * Fires when a cell is right clicked
58154          * @param {Grid} this
58155          * @param {Number} rowIndex
58156          * @param {Number} cellIndex
58157          * @param {Roo.EventObject} e
58158          */
58159          "cellcontextmenu" : true,
58160         /**
58161          * @event headercontextmenu
58162          * Fires when a header is right clicked
58163          * @param {Grid} this
58164          * @param {Number} columnIndex
58165          * @param {Roo.EventObject} e
58166          */
58167         "headercontextmenu" : true,
58168         /**
58169          * @event bodyscroll
58170          * Fires when the body element is scrolled
58171          * @param {Number} scrollLeft
58172          * @param {Number} scrollTop
58173          */
58174         "bodyscroll" : true,
58175         /**
58176          * @event columnresize
58177          * Fires when the user resizes a column
58178          * @param {Number} columnIndex
58179          * @param {Number} newSize
58180          */
58181         "columnresize" : true,
58182         /**
58183          * @event columnmove
58184          * Fires when the user moves a column
58185          * @param {Number} oldIndex
58186          * @param {Number} newIndex
58187          */
58188         "columnmove" : true,
58189         /**
58190          * @event startdrag
58191          * Fires when row(s) start being dragged
58192          * @param {Grid} this
58193          * @param {Roo.GridDD} dd The drag drop object
58194          * @param {event} e The raw browser event
58195          */
58196         "startdrag" : true,
58197         /**
58198          * @event enddrag
58199          * Fires when a drag operation is complete
58200          * @param {Grid} this
58201          * @param {Roo.GridDD} dd The drag drop object
58202          * @param {event} e The raw browser event
58203          */
58204         "enddrag" : true,
58205         /**
58206          * @event dragdrop
58207          * Fires when dragged row(s) are dropped on a valid DD target
58208          * @param {Grid} this
58209          * @param {Roo.GridDD} dd The drag drop object
58210          * @param {String} targetId The target drag drop object
58211          * @param {event} e The raw browser event
58212          */
58213         "dragdrop" : true,
58214         /**
58215          * @event dragover
58216          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58217          * @param {Grid} this
58218          * @param {Roo.GridDD} dd The drag drop object
58219          * @param {String} targetId The target drag drop object
58220          * @param {event} e The raw browser event
58221          */
58222         "dragover" : true,
58223         /**
58224          * @event dragenter
58225          *  Fires when the dragged row(s) first cross another DD target while being dragged
58226          * @param {Grid} this
58227          * @param {Roo.GridDD} dd The drag drop object
58228          * @param {String} targetId The target drag drop object
58229          * @param {event} e The raw browser event
58230          */
58231         "dragenter" : true,
58232         /**
58233          * @event dragout
58234          * Fires when the dragged row(s) leave another DD target while being dragged
58235          * @param {Grid} this
58236          * @param {Roo.GridDD} dd The drag drop object
58237          * @param {String} targetId The target drag drop object
58238          * @param {event} e The raw browser event
58239          */
58240         "dragout" : true,
58241         /**
58242          * @event rowclass
58243          * Fires when a row is rendered, so you can change add a style to it.
58244          * @param {GridView} gridview   The grid view
58245          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58246          */
58247         'rowclass' : true,
58248
58249         /**
58250          * @event render
58251          * Fires when the grid is rendered
58252          * @param {Grid} grid
58253          */
58254         'render' : true
58255     });
58256
58257     Roo.grid.Grid.superclass.constructor.call(this);
58258 };
58259 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58260     
58261     /**
58262          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58263          */
58264         /**
58265          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58266          */
58267         /**
58268          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58269          */
58270         /**
58271          * @cfg {Roo.grid.Store} ds The data store for the grid
58272          */
58273         /**
58274          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58275          */
58276         /**
58277      * @cfg {String} ddGroup - drag drop group.
58278      */
58279       /**
58280      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58281      */
58282
58283     /**
58284      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58285      */
58286     minColumnWidth : 25,
58287
58288     /**
58289      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58290      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58291      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58292      */
58293     autoSizeColumns : false,
58294
58295     /**
58296      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58297      */
58298     autoSizeHeaders : true,
58299
58300     /**
58301      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58302      */
58303     monitorWindowResize : true,
58304
58305     /**
58306      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58307      * rows measured to get a columns size. Default is 0 (all rows).
58308      */
58309     maxRowsToMeasure : 0,
58310
58311     /**
58312      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58313      */
58314     trackMouseOver : true,
58315
58316     /**
58317     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58318     */
58319       /**
58320     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58321     */
58322     
58323     /**
58324     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58325     */
58326     enableDragDrop : false,
58327     
58328     /**
58329     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58330     */
58331     enableColumnMove : true,
58332     
58333     /**
58334     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58335     */
58336     enableColumnHide : true,
58337     
58338     /**
58339     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58340     */
58341     enableRowHeightSync : false,
58342     
58343     /**
58344     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58345     */
58346     stripeRows : true,
58347     
58348     /**
58349     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58350     */
58351     autoHeight : false,
58352
58353     /**
58354      * @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.
58355      */
58356     autoExpandColumn : false,
58357
58358     /**
58359     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58360     * Default is 50.
58361     */
58362     autoExpandMin : 50,
58363
58364     /**
58365     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58366     */
58367     autoExpandMax : 1000,
58368
58369     /**
58370     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58371     */
58372     view : null,
58373
58374     /**
58375     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58376     */
58377     loadMask : false,
58378     /**
58379     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58380     */
58381     dropTarget: false,
58382      /**
58383     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58384     */ 
58385     sortColMenu : false,
58386     
58387     // private
58388     rendered : false,
58389
58390     /**
58391     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58392     * of a fixed width. Default is false.
58393     */
58394     /**
58395     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58396     */
58397     
58398     
58399     /**
58400     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58401     * %0 is replaced with the number of selected rows.
58402     */
58403     ddText : "{0} selected row{1}",
58404     
58405     
58406     /**
58407      * Called once after all setup has been completed and the grid is ready to be rendered.
58408      * @return {Roo.grid.Grid} this
58409      */
58410     render : function()
58411     {
58412         var c = this.container;
58413         // try to detect autoHeight/width mode
58414         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58415             this.autoHeight = true;
58416         }
58417         var view = this.getView();
58418         view.init(this);
58419
58420         c.on("click", this.onClick, this);
58421         c.on("dblclick", this.onDblClick, this);
58422         c.on("contextmenu", this.onContextMenu, this);
58423         c.on("keydown", this.onKeyDown, this);
58424         if (Roo.isTouch) {
58425             c.on("touchstart", this.onTouchStart, this);
58426         }
58427
58428         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58429
58430         this.getSelectionModel().init(this);
58431
58432         view.render();
58433
58434         if(this.loadMask){
58435             this.loadMask = new Roo.LoadMask(this.container,
58436                     Roo.apply({store:this.dataSource}, this.loadMask));
58437         }
58438         
58439         
58440         if (this.toolbar && this.toolbar.xtype) {
58441             this.toolbar.container = this.getView().getHeaderPanel(true);
58442             this.toolbar = new Roo.Toolbar(this.toolbar);
58443         }
58444         if (this.footer && this.footer.xtype) {
58445             this.footer.dataSource = this.getDataSource();
58446             this.footer.container = this.getView().getFooterPanel(true);
58447             this.footer = Roo.factory(this.footer, Roo);
58448         }
58449         if (this.dropTarget && this.dropTarget.xtype) {
58450             delete this.dropTarget.xtype;
58451             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58452         }
58453         
58454         
58455         this.rendered = true;
58456         this.fireEvent('render', this);
58457         return this;
58458     },
58459
58460     /**
58461      * Reconfigures the grid to use a different Store and Column Model.
58462      * The View will be bound to the new objects and refreshed.
58463      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58464      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58465      */
58466     reconfigure : function(dataSource, colModel){
58467         if(this.loadMask){
58468             this.loadMask.destroy();
58469             this.loadMask = new Roo.LoadMask(this.container,
58470                     Roo.apply({store:dataSource}, this.loadMask));
58471         }
58472         this.view.bind(dataSource, colModel);
58473         this.dataSource = dataSource;
58474         this.colModel = colModel;
58475         this.view.refresh(true);
58476     },
58477     /**
58478      * addColumns
58479      * Add's a column, default at the end..
58480      
58481      * @param {int} position to add (default end)
58482      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58483      */
58484     addColumns : function(pos, ar)
58485     {
58486         
58487         for (var i =0;i< ar.length;i++) {
58488             var cfg = ar[i];
58489             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58490             this.cm.lookup[cfg.id] = cfg;
58491         }
58492         
58493         
58494         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58495             pos = this.cm.config.length; //this.cm.config.push(cfg);
58496         } 
58497         pos = Math.max(0,pos);
58498         ar.unshift(0);
58499         ar.unshift(pos);
58500         this.cm.config.splice.apply(this.cm.config, ar);
58501         
58502         
58503         
58504         this.view.generateRules(this.cm);
58505         this.view.refresh(true);
58506         
58507     },
58508     
58509     
58510     
58511     
58512     // private
58513     onKeyDown : function(e){
58514         this.fireEvent("keydown", e);
58515     },
58516
58517     /**
58518      * Destroy this grid.
58519      * @param {Boolean} removeEl True to remove the element
58520      */
58521     destroy : function(removeEl, keepListeners){
58522         if(this.loadMask){
58523             this.loadMask.destroy();
58524         }
58525         var c = this.container;
58526         c.removeAllListeners();
58527         this.view.destroy();
58528         this.colModel.purgeListeners();
58529         if(!keepListeners){
58530             this.purgeListeners();
58531         }
58532         c.update("");
58533         if(removeEl === true){
58534             c.remove();
58535         }
58536     },
58537
58538     // private
58539     processEvent : function(name, e){
58540         // does this fire select???
58541         //Roo.log('grid:processEvent '  + name);
58542         
58543         if (name != 'touchstart' ) {
58544             this.fireEvent(name, e);    
58545         }
58546         
58547         var t = e.getTarget();
58548         var v = this.view;
58549         var header = v.findHeaderIndex(t);
58550         if(header !== false){
58551             var ename = name == 'touchstart' ? 'click' : name;
58552              
58553             this.fireEvent("header" + ename, this, header, e);
58554         }else{
58555             var row = v.findRowIndex(t);
58556             var cell = v.findCellIndex(t);
58557             if (name == 'touchstart') {
58558                 // first touch is always a click.
58559                 // hopefull this happens after selection is updated.?
58560                 name = false;
58561                 
58562                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58563                     var cs = this.selModel.getSelectedCell();
58564                     if (row == cs[0] && cell == cs[1]){
58565                         name = 'dblclick';
58566                     }
58567                 }
58568                 if (typeof(this.selModel.getSelections) != 'undefined') {
58569                     var cs = this.selModel.getSelections();
58570                     var ds = this.dataSource;
58571                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58572                         name = 'dblclick';
58573                     }
58574                 }
58575                 if (!name) {
58576                     return;
58577                 }
58578             }
58579             
58580             
58581             if(row !== false){
58582                 this.fireEvent("row" + name, this, row, e);
58583                 if(cell !== false){
58584                     this.fireEvent("cell" + name, this, row, cell, e);
58585                 }
58586             }
58587         }
58588     },
58589
58590     // private
58591     onClick : function(e){
58592         this.processEvent("click", e);
58593     },
58594    // private
58595     onTouchStart : function(e){
58596         this.processEvent("touchstart", e);
58597     },
58598
58599     // private
58600     onContextMenu : function(e, t){
58601         this.processEvent("contextmenu", e);
58602     },
58603
58604     // private
58605     onDblClick : function(e){
58606         this.processEvent("dblclick", e);
58607     },
58608
58609     // private
58610     walkCells : function(row, col, step, fn, scope){
58611         var cm = this.colModel, clen = cm.getColumnCount();
58612         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58613         if(step < 0){
58614             if(col < 0){
58615                 row--;
58616                 first = false;
58617             }
58618             while(row >= 0){
58619                 if(!first){
58620                     col = clen-1;
58621                 }
58622                 first = false;
58623                 while(col >= 0){
58624                     if(fn.call(scope || this, row, col, cm) === true){
58625                         return [row, col];
58626                     }
58627                     col--;
58628                 }
58629                 row--;
58630             }
58631         } else {
58632             if(col >= clen){
58633                 row++;
58634                 first = false;
58635             }
58636             while(row < rlen){
58637                 if(!first){
58638                     col = 0;
58639                 }
58640                 first = false;
58641                 while(col < clen){
58642                     if(fn.call(scope || this, row, col, cm) === true){
58643                         return [row, col];
58644                     }
58645                     col++;
58646                 }
58647                 row++;
58648             }
58649         }
58650         return null;
58651     },
58652
58653     // private
58654     getSelections : function(){
58655         return this.selModel.getSelections();
58656     },
58657
58658     /**
58659      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58660      * but if manual update is required this method will initiate it.
58661      */
58662     autoSize : function(){
58663         if(this.rendered){
58664             this.view.layout();
58665             if(this.view.adjustForScroll){
58666                 this.view.adjustForScroll();
58667             }
58668         }
58669     },
58670
58671     /**
58672      * Returns the grid's underlying element.
58673      * @return {Element} The element
58674      */
58675     getGridEl : function(){
58676         return this.container;
58677     },
58678
58679     // private for compatibility, overridden by editor grid
58680     stopEditing : function(){},
58681
58682     /**
58683      * Returns the grid's SelectionModel.
58684      * @return {SelectionModel}
58685      */
58686     getSelectionModel : function(){
58687         if(!this.selModel){
58688             this.selModel = new Roo.grid.RowSelectionModel();
58689         }
58690         return this.selModel;
58691     },
58692
58693     /**
58694      * Returns the grid's DataSource.
58695      * @return {DataSource}
58696      */
58697     getDataSource : function(){
58698         return this.dataSource;
58699     },
58700
58701     /**
58702      * Returns the grid's ColumnModel.
58703      * @return {ColumnModel}
58704      */
58705     getColumnModel : function(){
58706         return this.colModel;
58707     },
58708
58709     /**
58710      * Returns the grid's GridView object.
58711      * @return {GridView}
58712      */
58713     getView : function(){
58714         if(!this.view){
58715             this.view = new Roo.grid.GridView(this.viewConfig);
58716             this.relayEvents(this.view, [
58717                 "beforerowremoved", "beforerowsinserted",
58718                 "beforerefresh", "rowremoved",
58719                 "rowsinserted", "rowupdated" ,"refresh"
58720             ]);
58721         }
58722         return this.view;
58723     },
58724     /**
58725      * Called to get grid's drag proxy text, by default returns this.ddText.
58726      * Override this to put something different in the dragged text.
58727      * @return {String}
58728      */
58729     getDragDropText : function(){
58730         var count = this.selModel.getCount();
58731         return String.format(this.ddText, count, count == 1 ? '' : 's');
58732     }
58733 });
58734 /*
58735  * Based on:
58736  * Ext JS Library 1.1.1
58737  * Copyright(c) 2006-2007, Ext JS, LLC.
58738  *
58739  * Originally Released Under LGPL - original licence link has changed is not relivant.
58740  *
58741  * Fork - LGPL
58742  * <script type="text/javascript">
58743  */
58744  /**
58745  * @class Roo.grid.AbstractGridView
58746  * @extends Roo.util.Observable
58747  * @abstract
58748  * Abstract base class for grid Views
58749  * @constructor
58750  */
58751 Roo.grid.AbstractGridView = function(){
58752         this.grid = null;
58753         
58754         this.events = {
58755             "beforerowremoved" : true,
58756             "beforerowsinserted" : true,
58757             "beforerefresh" : true,
58758             "rowremoved" : true,
58759             "rowsinserted" : true,
58760             "rowupdated" : true,
58761             "refresh" : true
58762         };
58763     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58764 };
58765
58766 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58767     rowClass : "x-grid-row",
58768     cellClass : "x-grid-cell",
58769     tdClass : "x-grid-td",
58770     hdClass : "x-grid-hd",
58771     splitClass : "x-grid-hd-split",
58772     
58773     init: function(grid){
58774         this.grid = grid;
58775                 var cid = this.grid.getGridEl().id;
58776         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58777         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58778         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58779         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58780         },
58781         
58782     getColumnRenderers : function(){
58783         var renderers = [];
58784         var cm = this.grid.colModel;
58785         var colCount = cm.getColumnCount();
58786         for(var i = 0; i < colCount; i++){
58787             renderers[i] = cm.getRenderer(i);
58788         }
58789         return renderers;
58790     },
58791     
58792     getColumnIds : function(){
58793         var ids = [];
58794         var cm = this.grid.colModel;
58795         var colCount = cm.getColumnCount();
58796         for(var i = 0; i < colCount; i++){
58797             ids[i] = cm.getColumnId(i);
58798         }
58799         return ids;
58800     },
58801     
58802     getDataIndexes : function(){
58803         if(!this.indexMap){
58804             this.indexMap = this.buildIndexMap();
58805         }
58806         return this.indexMap.colToData;
58807     },
58808     
58809     getColumnIndexByDataIndex : function(dataIndex){
58810         if(!this.indexMap){
58811             this.indexMap = this.buildIndexMap();
58812         }
58813         return this.indexMap.dataToCol[dataIndex];
58814     },
58815     
58816     /**
58817      * Set a css style for a column dynamically. 
58818      * @param {Number} colIndex The index of the column
58819      * @param {String} name The css property name
58820      * @param {String} value The css value
58821      */
58822     setCSSStyle : function(colIndex, name, value){
58823         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58824         Roo.util.CSS.updateRule(selector, name, value);
58825     },
58826     
58827     generateRules : function(cm){
58828         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58829         Roo.util.CSS.removeStyleSheet(rulesId);
58830         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58831             var cid = cm.getColumnId(i);
58832             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58833                          this.tdSelector, cid, " {\n}\n",
58834                          this.hdSelector, cid, " {\n}\n",
58835                          this.splitSelector, cid, " {\n}\n");
58836         }
58837         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58838     }
58839 });/*
58840  * Based on:
58841  * Ext JS Library 1.1.1
58842  * Copyright(c) 2006-2007, Ext JS, LLC.
58843  *
58844  * Originally Released Under LGPL - original licence link has changed is not relivant.
58845  *
58846  * Fork - LGPL
58847  * <script type="text/javascript">
58848  */
58849
58850 // private
58851 // This is a support class used internally by the Grid components
58852 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58853     this.grid = grid;
58854     this.view = grid.getView();
58855     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58856     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58857     if(hd2){
58858         this.setHandleElId(Roo.id(hd));
58859         this.setOuterHandleElId(Roo.id(hd2));
58860     }
58861     this.scroll = false;
58862 };
58863 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58864     maxDragWidth: 120,
58865     getDragData : function(e){
58866         var t = Roo.lib.Event.getTarget(e);
58867         var h = this.view.findHeaderCell(t);
58868         if(h){
58869             return {ddel: h.firstChild, header:h};
58870         }
58871         return false;
58872     },
58873
58874     onInitDrag : function(e){
58875         this.view.headersDisabled = true;
58876         var clone = this.dragData.ddel.cloneNode(true);
58877         clone.id = Roo.id();
58878         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58879         this.proxy.update(clone);
58880         return true;
58881     },
58882
58883     afterValidDrop : function(){
58884         var v = this.view;
58885         setTimeout(function(){
58886             v.headersDisabled = false;
58887         }, 50);
58888     },
58889
58890     afterInvalidDrop : function(){
58891         var v = this.view;
58892         setTimeout(function(){
58893             v.headersDisabled = false;
58894         }, 50);
58895     }
58896 });
58897 /*
58898  * Based on:
58899  * Ext JS Library 1.1.1
58900  * Copyright(c) 2006-2007, Ext JS, LLC.
58901  *
58902  * Originally Released Under LGPL - original licence link has changed is not relivant.
58903  *
58904  * Fork - LGPL
58905  * <script type="text/javascript">
58906  */
58907 // private
58908 // This is a support class used internally by the Grid components
58909 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58910     this.grid = grid;
58911     this.view = grid.getView();
58912     // split the proxies so they don't interfere with mouse events
58913     this.proxyTop = Roo.DomHelper.append(document.body, {
58914         cls:"col-move-top", html:"&#160;"
58915     }, true);
58916     this.proxyBottom = Roo.DomHelper.append(document.body, {
58917         cls:"col-move-bottom", html:"&#160;"
58918     }, true);
58919     this.proxyTop.hide = this.proxyBottom.hide = function(){
58920         this.setLeftTop(-100,-100);
58921         this.setStyle("visibility", "hidden");
58922     };
58923     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58924     // temporarily disabled
58925     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58926     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58927 };
58928 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58929     proxyOffsets : [-4, -9],
58930     fly: Roo.Element.fly,
58931
58932     getTargetFromEvent : function(e){
58933         var t = Roo.lib.Event.getTarget(e);
58934         var cindex = this.view.findCellIndex(t);
58935         if(cindex !== false){
58936             return this.view.getHeaderCell(cindex);
58937         }
58938         return null;
58939     },
58940
58941     nextVisible : function(h){
58942         var v = this.view, cm = this.grid.colModel;
58943         h = h.nextSibling;
58944         while(h){
58945             if(!cm.isHidden(v.getCellIndex(h))){
58946                 return h;
58947             }
58948             h = h.nextSibling;
58949         }
58950         return null;
58951     },
58952
58953     prevVisible : function(h){
58954         var v = this.view, cm = this.grid.colModel;
58955         h = h.prevSibling;
58956         while(h){
58957             if(!cm.isHidden(v.getCellIndex(h))){
58958                 return h;
58959             }
58960             h = h.prevSibling;
58961         }
58962         return null;
58963     },
58964
58965     positionIndicator : function(h, n, e){
58966         var x = Roo.lib.Event.getPageX(e);
58967         var r = Roo.lib.Dom.getRegion(n.firstChild);
58968         var px, pt, py = r.top + this.proxyOffsets[1];
58969         if((r.right - x) <= (r.right-r.left)/2){
58970             px = r.right+this.view.borderWidth;
58971             pt = "after";
58972         }else{
58973             px = r.left;
58974             pt = "before";
58975         }
58976         var oldIndex = this.view.getCellIndex(h);
58977         var newIndex = this.view.getCellIndex(n);
58978
58979         if(this.grid.colModel.isFixed(newIndex)){
58980             return false;
58981         }
58982
58983         var locked = this.grid.colModel.isLocked(newIndex);
58984
58985         if(pt == "after"){
58986             newIndex++;
58987         }
58988         if(oldIndex < newIndex){
58989             newIndex--;
58990         }
58991         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58992             return false;
58993         }
58994         px +=  this.proxyOffsets[0];
58995         this.proxyTop.setLeftTop(px, py);
58996         this.proxyTop.show();
58997         if(!this.bottomOffset){
58998             this.bottomOffset = this.view.mainHd.getHeight();
58999         }
59000         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59001         this.proxyBottom.show();
59002         return pt;
59003     },
59004
59005     onNodeEnter : function(n, dd, e, data){
59006         if(data.header != n){
59007             this.positionIndicator(data.header, n, e);
59008         }
59009     },
59010
59011     onNodeOver : function(n, dd, e, data){
59012         var result = false;
59013         if(data.header != n){
59014             result = this.positionIndicator(data.header, n, e);
59015         }
59016         if(!result){
59017             this.proxyTop.hide();
59018             this.proxyBottom.hide();
59019         }
59020         return result ? this.dropAllowed : this.dropNotAllowed;
59021     },
59022
59023     onNodeOut : function(n, dd, e, data){
59024         this.proxyTop.hide();
59025         this.proxyBottom.hide();
59026     },
59027
59028     onNodeDrop : function(n, dd, e, data){
59029         var h = data.header;
59030         if(h != n){
59031             var cm = this.grid.colModel;
59032             var x = Roo.lib.Event.getPageX(e);
59033             var r = Roo.lib.Dom.getRegion(n.firstChild);
59034             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59035             var oldIndex = this.view.getCellIndex(h);
59036             var newIndex = this.view.getCellIndex(n);
59037             var locked = cm.isLocked(newIndex);
59038             if(pt == "after"){
59039                 newIndex++;
59040             }
59041             if(oldIndex < newIndex){
59042                 newIndex--;
59043             }
59044             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59045                 return false;
59046             }
59047             cm.setLocked(oldIndex, locked, true);
59048             cm.moveColumn(oldIndex, newIndex);
59049             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59050             return true;
59051         }
59052         return false;
59053     }
59054 });
59055 /*
59056  * Based on:
59057  * Ext JS Library 1.1.1
59058  * Copyright(c) 2006-2007, Ext JS, LLC.
59059  *
59060  * Originally Released Under LGPL - original licence link has changed is not relivant.
59061  *
59062  * Fork - LGPL
59063  * <script type="text/javascript">
59064  */
59065   
59066 /**
59067  * @class Roo.grid.GridView
59068  * @extends Roo.util.Observable
59069  *
59070  * @constructor
59071  * @param {Object} config
59072  */
59073 Roo.grid.GridView = function(config){
59074     Roo.grid.GridView.superclass.constructor.call(this);
59075     this.el = null;
59076
59077     Roo.apply(this, config);
59078 };
59079
59080 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59081
59082     unselectable :  'unselectable="on"',
59083     unselectableCls :  'x-unselectable',
59084     
59085     
59086     rowClass : "x-grid-row",
59087
59088     cellClass : "x-grid-col",
59089
59090     tdClass : "x-grid-td",
59091
59092     hdClass : "x-grid-hd",
59093
59094     splitClass : "x-grid-split",
59095
59096     sortClasses : ["sort-asc", "sort-desc"],
59097
59098     enableMoveAnim : false,
59099
59100     hlColor: "C3DAF9",
59101
59102     dh : Roo.DomHelper,
59103
59104     fly : Roo.Element.fly,
59105
59106     css : Roo.util.CSS,
59107
59108     borderWidth: 1,
59109
59110     splitOffset: 3,
59111
59112     scrollIncrement : 22,
59113
59114     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59115
59116     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59117
59118     bind : function(ds, cm){
59119         if(this.ds){
59120             this.ds.un("load", this.onLoad, this);
59121             this.ds.un("datachanged", this.onDataChange, this);
59122             this.ds.un("add", this.onAdd, this);
59123             this.ds.un("remove", this.onRemove, this);
59124             this.ds.un("update", this.onUpdate, this);
59125             this.ds.un("clear", this.onClear, this);
59126         }
59127         if(ds){
59128             ds.on("load", this.onLoad, this);
59129             ds.on("datachanged", this.onDataChange, this);
59130             ds.on("add", this.onAdd, this);
59131             ds.on("remove", this.onRemove, this);
59132             ds.on("update", this.onUpdate, this);
59133             ds.on("clear", this.onClear, this);
59134         }
59135         this.ds = ds;
59136
59137         if(this.cm){
59138             this.cm.un("widthchange", this.onColWidthChange, this);
59139             this.cm.un("headerchange", this.onHeaderChange, this);
59140             this.cm.un("hiddenchange", this.onHiddenChange, this);
59141             this.cm.un("columnmoved", this.onColumnMove, this);
59142             this.cm.un("columnlockchange", this.onColumnLock, this);
59143         }
59144         if(cm){
59145             this.generateRules(cm);
59146             cm.on("widthchange", this.onColWidthChange, this);
59147             cm.on("headerchange", this.onHeaderChange, this);
59148             cm.on("hiddenchange", this.onHiddenChange, this);
59149             cm.on("columnmoved", this.onColumnMove, this);
59150             cm.on("columnlockchange", this.onColumnLock, this);
59151         }
59152         this.cm = cm;
59153     },
59154
59155     init: function(grid){
59156         Roo.grid.GridView.superclass.init.call(this, grid);
59157
59158         this.bind(grid.dataSource, grid.colModel);
59159
59160         grid.on("headerclick", this.handleHeaderClick, this);
59161
59162         if(grid.trackMouseOver){
59163             grid.on("mouseover", this.onRowOver, this);
59164             grid.on("mouseout", this.onRowOut, this);
59165         }
59166         grid.cancelTextSelection = function(){};
59167         this.gridId = grid.id;
59168
59169         var tpls = this.templates || {};
59170
59171         if(!tpls.master){
59172             tpls.master = new Roo.Template(
59173                '<div class="x-grid" hidefocus="true">',
59174                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59175                   '<div class="x-grid-topbar"></div>',
59176                   '<div class="x-grid-scroller"><div></div></div>',
59177                   '<div class="x-grid-locked">',
59178                       '<div class="x-grid-header">{lockedHeader}</div>',
59179                       '<div class="x-grid-body">{lockedBody}</div>',
59180                   "</div>",
59181                   '<div class="x-grid-viewport">',
59182                       '<div class="x-grid-header">{header}</div>',
59183                       '<div class="x-grid-body">{body}</div>',
59184                   "</div>",
59185                   '<div class="x-grid-bottombar"></div>',
59186                  
59187                   '<div class="x-grid-resize-proxy">&#160;</div>',
59188                "</div>"
59189             );
59190             tpls.master.disableformats = true;
59191         }
59192
59193         if(!tpls.header){
59194             tpls.header = new Roo.Template(
59195                '<table border="0" cellspacing="0" cellpadding="0">',
59196                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59197                "</table>{splits}"
59198             );
59199             tpls.header.disableformats = true;
59200         }
59201         tpls.header.compile();
59202
59203         if(!tpls.hcell){
59204             tpls.hcell = new Roo.Template(
59205                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59206                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59207                 "</div></td>"
59208              );
59209              tpls.hcell.disableFormats = true;
59210         }
59211         tpls.hcell.compile();
59212
59213         if(!tpls.hsplit){
59214             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59215                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59216             tpls.hsplit.disableFormats = true;
59217         }
59218         tpls.hsplit.compile();
59219
59220         if(!tpls.body){
59221             tpls.body = new Roo.Template(
59222                '<table border="0" cellspacing="0" cellpadding="0">',
59223                "<tbody>{rows}</tbody>",
59224                "</table>"
59225             );
59226             tpls.body.disableFormats = true;
59227         }
59228         tpls.body.compile();
59229
59230         if(!tpls.row){
59231             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59232             tpls.row.disableFormats = true;
59233         }
59234         tpls.row.compile();
59235
59236         if(!tpls.cell){
59237             tpls.cell = new Roo.Template(
59238                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59239                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59240                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59241                 "</td>"
59242             );
59243             tpls.cell.disableFormats = true;
59244         }
59245         tpls.cell.compile();
59246
59247         this.templates = tpls;
59248     },
59249
59250     // remap these for backwards compat
59251     onColWidthChange : function(){
59252         this.updateColumns.apply(this, arguments);
59253     },
59254     onHeaderChange : function(){
59255         this.updateHeaders.apply(this, arguments);
59256     }, 
59257     onHiddenChange : function(){
59258         this.handleHiddenChange.apply(this, arguments);
59259     },
59260     onColumnMove : function(){
59261         this.handleColumnMove.apply(this, arguments);
59262     },
59263     onColumnLock : function(){
59264         this.handleLockChange.apply(this, arguments);
59265     },
59266
59267     onDataChange : function(){
59268         this.refresh();
59269         this.updateHeaderSortState();
59270     },
59271
59272     onClear : function(){
59273         this.refresh();
59274     },
59275
59276     onUpdate : function(ds, record){
59277         this.refreshRow(record);
59278     },
59279
59280     refreshRow : function(record){
59281         var ds = this.ds, index;
59282         if(typeof record == 'number'){
59283             index = record;
59284             record = ds.getAt(index);
59285         }else{
59286             index = ds.indexOf(record);
59287         }
59288         this.insertRows(ds, index, index, true);
59289         this.onRemove(ds, record, index+1, true);
59290         this.syncRowHeights(index, index);
59291         this.layout();
59292         this.fireEvent("rowupdated", this, index, record);
59293     },
59294
59295     onAdd : function(ds, records, index){
59296         this.insertRows(ds, index, index + (records.length-1));
59297     },
59298
59299     onRemove : function(ds, record, index, isUpdate){
59300         if(isUpdate !== true){
59301             this.fireEvent("beforerowremoved", this, index, record);
59302         }
59303         var bt = this.getBodyTable(), lt = this.getLockedTable();
59304         if(bt.rows[index]){
59305             bt.firstChild.removeChild(bt.rows[index]);
59306         }
59307         if(lt.rows[index]){
59308             lt.firstChild.removeChild(lt.rows[index]);
59309         }
59310         if(isUpdate !== true){
59311             this.stripeRows(index);
59312             this.syncRowHeights(index, index);
59313             this.layout();
59314             this.fireEvent("rowremoved", this, index, record);
59315         }
59316     },
59317
59318     onLoad : function(){
59319         this.scrollToTop();
59320     },
59321
59322     /**
59323      * Scrolls the grid to the top
59324      */
59325     scrollToTop : function(){
59326         if(this.scroller){
59327             this.scroller.dom.scrollTop = 0;
59328             this.syncScroll();
59329         }
59330     },
59331
59332     /**
59333      * Gets a panel in the header of the grid that can be used for toolbars etc.
59334      * After modifying the contents of this panel a call to grid.autoSize() may be
59335      * required to register any changes in size.
59336      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59337      * @return Roo.Element
59338      */
59339     getHeaderPanel : function(doShow){
59340         if(doShow){
59341             this.headerPanel.show();
59342         }
59343         return this.headerPanel;
59344     },
59345
59346     /**
59347      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59348      * After modifying the contents of this panel a call to grid.autoSize() may be
59349      * required to register any changes in size.
59350      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59351      * @return Roo.Element
59352      */
59353     getFooterPanel : function(doShow){
59354         if(doShow){
59355             this.footerPanel.show();
59356         }
59357         return this.footerPanel;
59358     },
59359
59360     initElements : function(){
59361         var E = Roo.Element;
59362         var el = this.grid.getGridEl().dom.firstChild;
59363         var cs = el.childNodes;
59364
59365         this.el = new E(el);
59366         
59367          this.focusEl = new E(el.firstChild);
59368         this.focusEl.swallowEvent("click", true);
59369         
59370         this.headerPanel = new E(cs[1]);
59371         this.headerPanel.enableDisplayMode("block");
59372
59373         this.scroller = new E(cs[2]);
59374         this.scrollSizer = new E(this.scroller.dom.firstChild);
59375
59376         this.lockedWrap = new E(cs[3]);
59377         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59378         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59379
59380         this.mainWrap = new E(cs[4]);
59381         this.mainHd = new E(this.mainWrap.dom.firstChild);
59382         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59383
59384         this.footerPanel = new E(cs[5]);
59385         this.footerPanel.enableDisplayMode("block");
59386
59387         this.resizeProxy = new E(cs[6]);
59388
59389         this.headerSelector = String.format(
59390            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59391            this.lockedHd.id, this.mainHd.id
59392         );
59393
59394         this.splitterSelector = String.format(
59395            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59396            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59397         );
59398     },
59399     idToCssName : function(s)
59400     {
59401         return s.replace(/[^a-z0-9]+/ig, '-');
59402     },
59403
59404     getHeaderCell : function(index){
59405         return Roo.DomQuery.select(this.headerSelector)[index];
59406     },
59407
59408     getHeaderCellMeasure : function(index){
59409         return this.getHeaderCell(index).firstChild;
59410     },
59411
59412     getHeaderCellText : function(index){
59413         return this.getHeaderCell(index).firstChild.firstChild;
59414     },
59415
59416     getLockedTable : function(){
59417         return this.lockedBody.dom.firstChild;
59418     },
59419
59420     getBodyTable : function(){
59421         return this.mainBody.dom.firstChild;
59422     },
59423
59424     getLockedRow : function(index){
59425         return this.getLockedTable().rows[index];
59426     },
59427
59428     getRow : function(index){
59429         return this.getBodyTable().rows[index];
59430     },
59431
59432     getRowComposite : function(index){
59433         if(!this.rowEl){
59434             this.rowEl = new Roo.CompositeElementLite();
59435         }
59436         var els = [], lrow, mrow;
59437         if(lrow = this.getLockedRow(index)){
59438             els.push(lrow);
59439         }
59440         if(mrow = this.getRow(index)){
59441             els.push(mrow);
59442         }
59443         this.rowEl.elements = els;
59444         return this.rowEl;
59445     },
59446     /**
59447      * Gets the 'td' of the cell
59448      * 
59449      * @param {Integer} rowIndex row to select
59450      * @param {Integer} colIndex column to select
59451      * 
59452      * @return {Object} 
59453      */
59454     getCell : function(rowIndex, colIndex){
59455         var locked = this.cm.getLockedCount();
59456         var source;
59457         if(colIndex < locked){
59458             source = this.lockedBody.dom.firstChild;
59459         }else{
59460             source = this.mainBody.dom.firstChild;
59461             colIndex -= locked;
59462         }
59463         return source.rows[rowIndex].childNodes[colIndex];
59464     },
59465
59466     getCellText : function(rowIndex, colIndex){
59467         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59468     },
59469
59470     getCellBox : function(cell){
59471         var b = this.fly(cell).getBox();
59472         if(Roo.isOpera){ // opera fails to report the Y
59473             b.y = cell.offsetTop + this.mainBody.getY();
59474         }
59475         return b;
59476     },
59477
59478     getCellIndex : function(cell){
59479         var id = String(cell.className).match(this.cellRE);
59480         if(id){
59481             return parseInt(id[1], 10);
59482         }
59483         return 0;
59484     },
59485
59486     findHeaderIndex : function(n){
59487         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59488         return r ? this.getCellIndex(r) : false;
59489     },
59490
59491     findHeaderCell : function(n){
59492         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59493         return r ? r : false;
59494     },
59495
59496     findRowIndex : function(n){
59497         if(!n){
59498             return false;
59499         }
59500         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59501         return r ? r.rowIndex : false;
59502     },
59503
59504     findCellIndex : function(node){
59505         var stop = this.el.dom;
59506         while(node && node != stop){
59507             if(this.findRE.test(node.className)){
59508                 return this.getCellIndex(node);
59509             }
59510             node = node.parentNode;
59511         }
59512         return false;
59513     },
59514
59515     getColumnId : function(index){
59516         return this.cm.getColumnId(index);
59517     },
59518
59519     getSplitters : function()
59520     {
59521         if(this.splitterSelector){
59522            return Roo.DomQuery.select(this.splitterSelector);
59523         }else{
59524             return null;
59525       }
59526     },
59527
59528     getSplitter : function(index){
59529         return this.getSplitters()[index];
59530     },
59531
59532     onRowOver : function(e, t){
59533         var row;
59534         if((row = this.findRowIndex(t)) !== false){
59535             this.getRowComposite(row).addClass("x-grid-row-over");
59536         }
59537     },
59538
59539     onRowOut : function(e, t){
59540         var row;
59541         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59542             this.getRowComposite(row).removeClass("x-grid-row-over");
59543         }
59544     },
59545
59546     renderHeaders : function(){
59547         var cm = this.cm;
59548         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59549         var cb = [], lb = [], sb = [], lsb = [], p = {};
59550         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59551             p.cellId = "x-grid-hd-0-" + i;
59552             p.splitId = "x-grid-csplit-0-" + i;
59553             p.id = cm.getColumnId(i);
59554             p.value = cm.getColumnHeader(i) || "";
59555             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59556             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59557             if(!cm.isLocked(i)){
59558                 cb[cb.length] = ct.apply(p);
59559                 sb[sb.length] = st.apply(p);
59560             }else{
59561                 lb[lb.length] = ct.apply(p);
59562                 lsb[lsb.length] = st.apply(p);
59563             }
59564         }
59565         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59566                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59567     },
59568
59569     updateHeaders : function(){
59570         var html = this.renderHeaders();
59571         this.lockedHd.update(html[0]);
59572         this.mainHd.update(html[1]);
59573     },
59574
59575     /**
59576      * Focuses the specified row.
59577      * @param {Number} row The row index
59578      */
59579     focusRow : function(row)
59580     {
59581         //Roo.log('GridView.focusRow');
59582         var x = this.scroller.dom.scrollLeft;
59583         this.focusCell(row, 0, false);
59584         this.scroller.dom.scrollLeft = x;
59585     },
59586
59587     /**
59588      * Focuses the specified cell.
59589      * @param {Number} row The row index
59590      * @param {Number} col The column index
59591      * @param {Boolean} hscroll false to disable horizontal scrolling
59592      */
59593     focusCell : function(row, col, hscroll)
59594     {
59595         //Roo.log('GridView.focusCell');
59596         var el = this.ensureVisible(row, col, hscroll);
59597         this.focusEl.alignTo(el, "tl-tl");
59598         if(Roo.isGecko){
59599             this.focusEl.focus();
59600         }else{
59601             this.focusEl.focus.defer(1, this.focusEl);
59602         }
59603     },
59604
59605     /**
59606      * Scrolls the specified cell into view
59607      * @param {Number} row The row index
59608      * @param {Number} col The column index
59609      * @param {Boolean} hscroll false to disable horizontal scrolling
59610      */
59611     ensureVisible : function(row, col, hscroll)
59612     {
59613         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59614         //return null; //disable for testing.
59615         if(typeof row != "number"){
59616             row = row.rowIndex;
59617         }
59618         if(row < 0 && row >= this.ds.getCount()){
59619             return  null;
59620         }
59621         col = (col !== undefined ? col : 0);
59622         var cm = this.grid.colModel;
59623         while(cm.isHidden(col)){
59624             col++;
59625         }
59626
59627         var el = this.getCell(row, col);
59628         if(!el){
59629             return null;
59630         }
59631         var c = this.scroller.dom;
59632
59633         var ctop = parseInt(el.offsetTop, 10);
59634         var cleft = parseInt(el.offsetLeft, 10);
59635         var cbot = ctop + el.offsetHeight;
59636         var cright = cleft + el.offsetWidth;
59637         
59638         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59639         var stop = parseInt(c.scrollTop, 10);
59640         var sleft = parseInt(c.scrollLeft, 10);
59641         var sbot = stop + ch;
59642         var sright = sleft + c.clientWidth;
59643         /*
59644         Roo.log('GridView.ensureVisible:' +
59645                 ' ctop:' + ctop +
59646                 ' c.clientHeight:' + c.clientHeight +
59647                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59648                 ' stop:' + stop +
59649                 ' cbot:' + cbot +
59650                 ' sbot:' + sbot +
59651                 ' ch:' + ch  
59652                 );
59653         */
59654         if(ctop < stop){
59655             c.scrollTop = ctop;
59656             //Roo.log("set scrolltop to ctop DISABLE?");
59657         }else if(cbot > sbot){
59658             //Roo.log("set scrolltop to cbot-ch");
59659             c.scrollTop = cbot-ch;
59660         }
59661         
59662         if(hscroll !== false){
59663             if(cleft < sleft){
59664                 c.scrollLeft = cleft;
59665             }else if(cright > sright){
59666                 c.scrollLeft = cright-c.clientWidth;
59667             }
59668         }
59669          
59670         return el;
59671     },
59672
59673     updateColumns : function(){
59674         this.grid.stopEditing();
59675         var cm = this.grid.colModel, colIds = this.getColumnIds();
59676         //var totalWidth = cm.getTotalWidth();
59677         var pos = 0;
59678         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59679             //if(cm.isHidden(i)) continue;
59680             var w = cm.getColumnWidth(i);
59681             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59682             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59683         }
59684         this.updateSplitters();
59685     },
59686
59687     generateRules : function(cm){
59688         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59689         Roo.util.CSS.removeStyleSheet(rulesId);
59690         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59691             var cid = cm.getColumnId(i);
59692             var align = '';
59693             if(cm.config[i].align){
59694                 align = 'text-align:'+cm.config[i].align+';';
59695             }
59696             var hidden = '';
59697             if(cm.isHidden(i)){
59698                 hidden = 'display:none;';
59699             }
59700             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59701             ruleBuf.push(
59702                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59703                     this.hdSelector, cid, " {\n", align, width, "}\n",
59704                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59705                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59706         }
59707         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59708     },
59709
59710     updateSplitters : function(){
59711         var cm = this.cm, s = this.getSplitters();
59712         if(s){ // splitters not created yet
59713             var pos = 0, locked = true;
59714             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59715                 if(cm.isHidden(i)) {
59716                     continue;
59717                 }
59718                 var w = cm.getColumnWidth(i); // make sure it's a number
59719                 if(!cm.isLocked(i) && locked){
59720                     pos = 0;
59721                     locked = false;
59722                 }
59723                 pos += w;
59724                 s[i].style.left = (pos-this.splitOffset) + "px";
59725             }
59726         }
59727     },
59728
59729     handleHiddenChange : function(colModel, colIndex, hidden){
59730         if(hidden){
59731             this.hideColumn(colIndex);
59732         }else{
59733             this.unhideColumn(colIndex);
59734         }
59735     },
59736
59737     hideColumn : function(colIndex){
59738         var cid = this.getColumnId(colIndex);
59739         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59740         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59741         if(Roo.isSafari){
59742             this.updateHeaders();
59743         }
59744         this.updateSplitters();
59745         this.layout();
59746     },
59747
59748     unhideColumn : function(colIndex){
59749         var cid = this.getColumnId(colIndex);
59750         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59751         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59752
59753         if(Roo.isSafari){
59754             this.updateHeaders();
59755         }
59756         this.updateSplitters();
59757         this.layout();
59758     },
59759
59760     insertRows : function(dm, firstRow, lastRow, isUpdate){
59761         if(firstRow == 0 && lastRow == dm.getCount()-1){
59762             this.refresh();
59763         }else{
59764             if(!isUpdate){
59765                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59766             }
59767             var s = this.getScrollState();
59768             var markup = this.renderRows(firstRow, lastRow);
59769             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59770             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59771             this.restoreScroll(s);
59772             if(!isUpdate){
59773                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59774                 this.syncRowHeights(firstRow, lastRow);
59775                 this.stripeRows(firstRow);
59776                 this.layout();
59777             }
59778         }
59779     },
59780
59781     bufferRows : function(markup, target, index){
59782         var before = null, trows = target.rows, tbody = target.tBodies[0];
59783         if(index < trows.length){
59784             before = trows[index];
59785         }
59786         var b = document.createElement("div");
59787         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59788         var rows = b.firstChild.rows;
59789         for(var i = 0, len = rows.length; i < len; i++){
59790             if(before){
59791                 tbody.insertBefore(rows[0], before);
59792             }else{
59793                 tbody.appendChild(rows[0]);
59794             }
59795         }
59796         b.innerHTML = "";
59797         b = null;
59798     },
59799
59800     deleteRows : function(dm, firstRow, lastRow){
59801         if(dm.getRowCount()<1){
59802             this.fireEvent("beforerefresh", this);
59803             this.mainBody.update("");
59804             this.lockedBody.update("");
59805             this.fireEvent("refresh", this);
59806         }else{
59807             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59808             var bt = this.getBodyTable();
59809             var tbody = bt.firstChild;
59810             var rows = bt.rows;
59811             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59812                 tbody.removeChild(rows[firstRow]);
59813             }
59814             this.stripeRows(firstRow);
59815             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59816         }
59817     },
59818
59819     updateRows : function(dataSource, firstRow, lastRow){
59820         var s = this.getScrollState();
59821         this.refresh();
59822         this.restoreScroll(s);
59823     },
59824
59825     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59826         if(!noRefresh){
59827            this.refresh();
59828         }
59829         this.updateHeaderSortState();
59830     },
59831
59832     getScrollState : function(){
59833         
59834         var sb = this.scroller.dom;
59835         return {left: sb.scrollLeft, top: sb.scrollTop};
59836     },
59837
59838     stripeRows : function(startRow){
59839         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59840             return;
59841         }
59842         startRow = startRow || 0;
59843         var rows = this.getBodyTable().rows;
59844         var lrows = this.getLockedTable().rows;
59845         var cls = ' x-grid-row-alt ';
59846         for(var i = startRow, len = rows.length; i < len; i++){
59847             var row = rows[i], lrow = lrows[i];
59848             var isAlt = ((i+1) % 2 == 0);
59849             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59850             if(isAlt == hasAlt){
59851                 continue;
59852             }
59853             if(isAlt){
59854                 row.className += " x-grid-row-alt";
59855             }else{
59856                 row.className = row.className.replace("x-grid-row-alt", "");
59857             }
59858             if(lrow){
59859                 lrow.className = row.className;
59860             }
59861         }
59862     },
59863
59864     restoreScroll : function(state){
59865         //Roo.log('GridView.restoreScroll');
59866         var sb = this.scroller.dom;
59867         sb.scrollLeft = state.left;
59868         sb.scrollTop = state.top;
59869         this.syncScroll();
59870     },
59871
59872     syncScroll : function(){
59873         //Roo.log('GridView.syncScroll');
59874         var sb = this.scroller.dom;
59875         var sh = this.mainHd.dom;
59876         var bs = this.mainBody.dom;
59877         var lv = this.lockedBody.dom;
59878         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59879         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59880     },
59881
59882     handleScroll : function(e){
59883         this.syncScroll();
59884         var sb = this.scroller.dom;
59885         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59886         e.stopEvent();
59887     },
59888
59889     handleWheel : function(e){
59890         var d = e.getWheelDelta();
59891         this.scroller.dom.scrollTop -= d*22;
59892         // set this here to prevent jumpy scrolling on large tables
59893         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59894         e.stopEvent();
59895     },
59896
59897     renderRows : function(startRow, endRow){
59898         // pull in all the crap needed to render rows
59899         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59900         var colCount = cm.getColumnCount();
59901
59902         if(ds.getCount() < 1){
59903             return ["", ""];
59904         }
59905
59906         // build a map for all the columns
59907         var cs = [];
59908         for(var i = 0; i < colCount; i++){
59909             var name = cm.getDataIndex(i);
59910             cs[i] = {
59911                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59912                 renderer : cm.getRenderer(i),
59913                 id : cm.getColumnId(i),
59914                 locked : cm.isLocked(i),
59915                 has_editor : cm.isCellEditable(i)
59916             };
59917         }
59918
59919         startRow = startRow || 0;
59920         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59921
59922         // records to render
59923         var rs = ds.getRange(startRow, endRow);
59924
59925         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59926     },
59927
59928     // As much as I hate to duplicate code, this was branched because FireFox really hates
59929     // [].join("") on strings. The performance difference was substantial enough to
59930     // branch this function
59931     doRender : Roo.isGecko ?
59932             function(cs, rs, ds, startRow, colCount, stripe){
59933                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59934                 // buffers
59935                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59936                 
59937                 var hasListener = this.grid.hasListener('rowclass');
59938                 var rowcfg = {};
59939                 for(var j = 0, len = rs.length; j < len; j++){
59940                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59941                     for(var i = 0; i < colCount; i++){
59942                         c = cs[i];
59943                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59944                         p.id = c.id;
59945                         p.css = p.attr = "";
59946                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59947                         if(p.value == undefined || p.value === "") {
59948                             p.value = "&#160;";
59949                         }
59950                         if(c.has_editor){
59951                             p.css += ' x-grid-editable-cell';
59952                         }
59953                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59954                             p.css +=  ' x-grid-dirty-cell';
59955                         }
59956                         var markup = ct.apply(p);
59957                         if(!c.locked){
59958                             cb+= markup;
59959                         }else{
59960                             lcb+= markup;
59961                         }
59962                     }
59963                     var alt = [];
59964                     if(stripe && ((rowIndex+1) % 2 == 0)){
59965                         alt.push("x-grid-row-alt")
59966                     }
59967                     if(r.dirty){
59968                         alt.push(  " x-grid-dirty-row");
59969                     }
59970                     rp.cells = lcb;
59971                     if(this.getRowClass){
59972                         alt.push(this.getRowClass(r, rowIndex));
59973                     }
59974                     if (hasListener) {
59975                         rowcfg = {
59976                              
59977                             record: r,
59978                             rowIndex : rowIndex,
59979                             rowClass : ''
59980                         };
59981                         this.grid.fireEvent('rowclass', this, rowcfg);
59982                         alt.push(rowcfg.rowClass);
59983                     }
59984                     rp.alt = alt.join(" ");
59985                     lbuf+= rt.apply(rp);
59986                     rp.cells = cb;
59987                     buf+=  rt.apply(rp);
59988                 }
59989                 return [lbuf, buf];
59990             } :
59991             function(cs, rs, ds, startRow, colCount, stripe){
59992                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59993                 // buffers
59994                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59995                 var hasListener = this.grid.hasListener('rowclass');
59996  
59997                 var rowcfg = {};
59998                 for(var j = 0, len = rs.length; j < len; j++){
59999                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60000                     for(var i = 0; i < colCount; i++){
60001                         c = cs[i];
60002                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60003                         p.id = c.id;
60004                         p.css = p.attr = "";
60005                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60006                         if(p.value == undefined || p.value === "") {
60007                             p.value = "&#160;";
60008                         }
60009                         //Roo.log(c);
60010                          if(c.has_editor){
60011                             p.css += ' x-grid-editable-cell';
60012                         }
60013                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60014                             p.css += ' x-grid-dirty-cell' 
60015                         }
60016                         
60017                         var markup = ct.apply(p);
60018                         if(!c.locked){
60019                             cb[cb.length] = markup;
60020                         }else{
60021                             lcb[lcb.length] = markup;
60022                         }
60023                     }
60024                     var alt = [];
60025                     if(stripe && ((rowIndex+1) % 2 == 0)){
60026                         alt.push( "x-grid-row-alt");
60027                     }
60028                     if(r.dirty){
60029                         alt.push(" x-grid-dirty-row");
60030                     }
60031                     rp.cells = lcb;
60032                     if(this.getRowClass){
60033                         alt.push( this.getRowClass(r, rowIndex));
60034                     }
60035                     if (hasListener) {
60036                         rowcfg = {
60037                              
60038                             record: r,
60039                             rowIndex : rowIndex,
60040                             rowClass : ''
60041                         };
60042                         this.grid.fireEvent('rowclass', this, rowcfg);
60043                         alt.push(rowcfg.rowClass);
60044                     }
60045                     
60046                     rp.alt = alt.join(" ");
60047                     rp.cells = lcb.join("");
60048                     lbuf[lbuf.length] = rt.apply(rp);
60049                     rp.cells = cb.join("");
60050                     buf[buf.length] =  rt.apply(rp);
60051                 }
60052                 return [lbuf.join(""), buf.join("")];
60053             },
60054
60055     renderBody : function(){
60056         var markup = this.renderRows();
60057         var bt = this.templates.body;
60058         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60059     },
60060
60061     /**
60062      * Refreshes the grid
60063      * @param {Boolean} headersToo
60064      */
60065     refresh : function(headersToo){
60066         this.fireEvent("beforerefresh", this);
60067         this.grid.stopEditing();
60068         var result = this.renderBody();
60069         this.lockedBody.update(result[0]);
60070         this.mainBody.update(result[1]);
60071         if(headersToo === true){
60072             this.updateHeaders();
60073             this.updateColumns();
60074             this.updateSplitters();
60075             this.updateHeaderSortState();
60076         }
60077         this.syncRowHeights();
60078         this.layout();
60079         this.fireEvent("refresh", this);
60080     },
60081
60082     handleColumnMove : function(cm, oldIndex, newIndex){
60083         this.indexMap = null;
60084         var s = this.getScrollState();
60085         this.refresh(true);
60086         this.restoreScroll(s);
60087         this.afterMove(newIndex);
60088     },
60089
60090     afterMove : function(colIndex){
60091         if(this.enableMoveAnim && Roo.enableFx){
60092             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60093         }
60094         // if multisort - fix sortOrder, and reload..
60095         if (this.grid.dataSource.multiSort) {
60096             // the we can call sort again..
60097             var dm = this.grid.dataSource;
60098             var cm = this.grid.colModel;
60099             var so = [];
60100             for(var i = 0; i < cm.config.length; i++ ) {
60101                 
60102                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60103                     continue; // dont' bother, it's not in sort list or being set.
60104                 }
60105                 
60106                 so.push(cm.config[i].dataIndex);
60107             };
60108             dm.sortOrder = so;
60109             dm.load(dm.lastOptions);
60110             
60111             
60112         }
60113         
60114     },
60115
60116     updateCell : function(dm, rowIndex, dataIndex){
60117         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60118         if(typeof colIndex == "undefined"){ // not present in grid
60119             return;
60120         }
60121         var cm = this.grid.colModel;
60122         var cell = this.getCell(rowIndex, colIndex);
60123         var cellText = this.getCellText(rowIndex, colIndex);
60124
60125         var p = {
60126             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60127             id : cm.getColumnId(colIndex),
60128             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60129         };
60130         var renderer = cm.getRenderer(colIndex);
60131         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60132         if(typeof val == "undefined" || val === "") {
60133             val = "&#160;";
60134         }
60135         cellText.innerHTML = val;
60136         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60137         this.syncRowHeights(rowIndex, rowIndex);
60138     },
60139
60140     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60141         var maxWidth = 0;
60142         if(this.grid.autoSizeHeaders){
60143             var h = this.getHeaderCellMeasure(colIndex);
60144             maxWidth = Math.max(maxWidth, h.scrollWidth);
60145         }
60146         var tb, index;
60147         if(this.cm.isLocked(colIndex)){
60148             tb = this.getLockedTable();
60149             index = colIndex;
60150         }else{
60151             tb = this.getBodyTable();
60152             index = colIndex - this.cm.getLockedCount();
60153         }
60154         if(tb && tb.rows){
60155             var rows = tb.rows;
60156             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60157             for(var i = 0; i < stopIndex; i++){
60158                 var cell = rows[i].childNodes[index].firstChild;
60159                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60160             }
60161         }
60162         return maxWidth + /*margin for error in IE*/ 5;
60163     },
60164     /**
60165      * Autofit a column to its content.
60166      * @param {Number} colIndex
60167      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60168      */
60169      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60170          if(this.cm.isHidden(colIndex)){
60171              return; // can't calc a hidden column
60172          }
60173         if(forceMinSize){
60174             var cid = this.cm.getColumnId(colIndex);
60175             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60176            if(this.grid.autoSizeHeaders){
60177                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60178            }
60179         }
60180         var newWidth = this.calcColumnWidth(colIndex);
60181         this.cm.setColumnWidth(colIndex,
60182             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60183         if(!suppressEvent){
60184             this.grid.fireEvent("columnresize", colIndex, newWidth);
60185         }
60186     },
60187
60188     /**
60189      * Autofits all columns to their content and then expands to fit any extra space in the grid
60190      */
60191      autoSizeColumns : function(){
60192         var cm = this.grid.colModel;
60193         var colCount = cm.getColumnCount();
60194         for(var i = 0; i < colCount; i++){
60195             this.autoSizeColumn(i, true, true);
60196         }
60197         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60198             this.fitColumns();
60199         }else{
60200             this.updateColumns();
60201             this.layout();
60202         }
60203     },
60204
60205     /**
60206      * Autofits all columns to the grid's width proportionate with their current size
60207      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60208      */
60209     fitColumns : function(reserveScrollSpace){
60210         var cm = this.grid.colModel;
60211         var colCount = cm.getColumnCount();
60212         var cols = [];
60213         var width = 0;
60214         var i, w;
60215         for (i = 0; i < colCount; i++){
60216             if(!cm.isHidden(i) && !cm.isFixed(i)){
60217                 w = cm.getColumnWidth(i);
60218                 cols.push(i);
60219                 cols.push(w);
60220                 width += w;
60221             }
60222         }
60223         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60224         if(reserveScrollSpace){
60225             avail -= 17;
60226         }
60227         var frac = (avail - cm.getTotalWidth())/width;
60228         while (cols.length){
60229             w = cols.pop();
60230             i = cols.pop();
60231             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60232         }
60233         this.updateColumns();
60234         this.layout();
60235     },
60236
60237     onRowSelect : function(rowIndex){
60238         var row = this.getRowComposite(rowIndex);
60239         row.addClass("x-grid-row-selected");
60240     },
60241
60242     onRowDeselect : function(rowIndex){
60243         var row = this.getRowComposite(rowIndex);
60244         row.removeClass("x-grid-row-selected");
60245     },
60246
60247     onCellSelect : function(row, col){
60248         var cell = this.getCell(row, col);
60249         if(cell){
60250             Roo.fly(cell).addClass("x-grid-cell-selected");
60251         }
60252     },
60253
60254     onCellDeselect : function(row, col){
60255         var cell = this.getCell(row, col);
60256         if(cell){
60257             Roo.fly(cell).removeClass("x-grid-cell-selected");
60258         }
60259     },
60260
60261     updateHeaderSortState : function(){
60262         
60263         // sort state can be single { field: xxx, direction : yyy}
60264         // or   { xxx=>ASC , yyy : DESC ..... }
60265         
60266         var mstate = {};
60267         if (!this.ds.multiSort) { 
60268             var state = this.ds.getSortState();
60269             if(!state){
60270                 return;
60271             }
60272             mstate[state.field] = state.direction;
60273             // FIXME... - this is not used here.. but might be elsewhere..
60274             this.sortState = state;
60275             
60276         } else {
60277             mstate = this.ds.sortToggle;
60278         }
60279         //remove existing sort classes..
60280         
60281         var sc = this.sortClasses;
60282         var hds = this.el.select(this.headerSelector).removeClass(sc);
60283         
60284         for(var f in mstate) {
60285         
60286             var sortColumn = this.cm.findColumnIndex(f);
60287             
60288             if(sortColumn != -1){
60289                 var sortDir = mstate[f];        
60290                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60291             }
60292         }
60293         
60294          
60295         
60296     },
60297
60298
60299     handleHeaderClick : function(g, index,e){
60300         
60301         Roo.log("header click");
60302         
60303         if (Roo.isTouch) {
60304             // touch events on header are handled by context
60305             this.handleHdCtx(g,index,e);
60306             return;
60307         }
60308         
60309         
60310         if(this.headersDisabled){
60311             return;
60312         }
60313         var dm = g.dataSource, cm = g.colModel;
60314         if(!cm.isSortable(index)){
60315             return;
60316         }
60317         g.stopEditing();
60318         
60319         if (dm.multiSort) {
60320             // update the sortOrder
60321             var so = [];
60322             for(var i = 0; i < cm.config.length; i++ ) {
60323                 
60324                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60325                     continue; // dont' bother, it's not in sort list or being set.
60326                 }
60327                 
60328                 so.push(cm.config[i].dataIndex);
60329             };
60330             dm.sortOrder = so;
60331         }
60332         
60333         
60334         dm.sort(cm.getDataIndex(index));
60335     },
60336
60337
60338     destroy : function(){
60339         if(this.colMenu){
60340             this.colMenu.removeAll();
60341             Roo.menu.MenuMgr.unregister(this.colMenu);
60342             this.colMenu.getEl().remove();
60343             delete this.colMenu;
60344         }
60345         if(this.hmenu){
60346             this.hmenu.removeAll();
60347             Roo.menu.MenuMgr.unregister(this.hmenu);
60348             this.hmenu.getEl().remove();
60349             delete this.hmenu;
60350         }
60351         if(this.grid.enableColumnMove){
60352             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60353             if(dds){
60354                 for(var dd in dds){
60355                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60356                         var elid = dds[dd].dragElId;
60357                         dds[dd].unreg();
60358                         Roo.get(elid).remove();
60359                     } else if(dds[dd].config.isTarget){
60360                         dds[dd].proxyTop.remove();
60361                         dds[dd].proxyBottom.remove();
60362                         dds[dd].unreg();
60363                     }
60364                     if(Roo.dd.DDM.locationCache[dd]){
60365                         delete Roo.dd.DDM.locationCache[dd];
60366                     }
60367                 }
60368                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60369             }
60370         }
60371         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60372         this.bind(null, null);
60373         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60374     },
60375
60376     handleLockChange : function(){
60377         this.refresh(true);
60378     },
60379
60380     onDenyColumnLock : function(){
60381
60382     },
60383
60384     onDenyColumnHide : function(){
60385
60386     },
60387
60388     handleHdMenuClick : function(item){
60389         var index = this.hdCtxIndex;
60390         var cm = this.cm, ds = this.ds;
60391         switch(item.id){
60392             case "asc":
60393                 ds.sort(cm.getDataIndex(index), "ASC");
60394                 break;
60395             case "desc":
60396                 ds.sort(cm.getDataIndex(index), "DESC");
60397                 break;
60398             case "lock":
60399                 var lc = cm.getLockedCount();
60400                 if(cm.getColumnCount(true) <= lc+1){
60401                     this.onDenyColumnLock();
60402                     return;
60403                 }
60404                 if(lc != index){
60405                     cm.setLocked(index, true, true);
60406                     cm.moveColumn(index, lc);
60407                     this.grid.fireEvent("columnmove", index, lc);
60408                 }else{
60409                     cm.setLocked(index, true);
60410                 }
60411             break;
60412             case "unlock":
60413                 var lc = cm.getLockedCount();
60414                 if((lc-1) != index){
60415                     cm.setLocked(index, false, true);
60416                     cm.moveColumn(index, lc-1);
60417                     this.grid.fireEvent("columnmove", index, lc-1);
60418                 }else{
60419                     cm.setLocked(index, false);
60420                 }
60421             break;
60422             case 'wider': // used to expand cols on touch..
60423             case 'narrow':
60424                 var cw = cm.getColumnWidth(index);
60425                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60426                 cw = Math.max(0, cw);
60427                 cw = Math.min(cw,4000);
60428                 cm.setColumnWidth(index, cw);
60429                 break;
60430                 
60431             default:
60432                 index = cm.getIndexById(item.id.substr(4));
60433                 if(index != -1){
60434                     if(item.checked && cm.getColumnCount(true) <= 1){
60435                         this.onDenyColumnHide();
60436                         return false;
60437                     }
60438                     cm.setHidden(index, item.checked);
60439                 }
60440         }
60441         return true;
60442     },
60443
60444     beforeColMenuShow : function(){
60445         var cm = this.cm,  colCount = cm.getColumnCount();
60446         this.colMenu.removeAll();
60447         
60448         var items = [];
60449         for(var i = 0; i < colCount; i++){
60450             items.push({
60451                 id: "col-"+cm.getColumnId(i),
60452                 text: cm.getColumnHeader(i),
60453                 checked: !cm.isHidden(i),
60454                 hideOnClick:false
60455             });
60456         }
60457         
60458         if (this.grid.sortColMenu) {
60459             items.sort(function(a,b) {
60460                 if (a.text == b.text) {
60461                     return 0;
60462                 }
60463                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60464             });
60465         }
60466         
60467         for(var i = 0; i < colCount; i++){
60468             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60469         }
60470     },
60471
60472     handleHdCtx : function(g, index, e){
60473         e.stopEvent();
60474         var hd = this.getHeaderCell(index);
60475         this.hdCtxIndex = index;
60476         var ms = this.hmenu.items, cm = this.cm;
60477         ms.get("asc").setDisabled(!cm.isSortable(index));
60478         ms.get("desc").setDisabled(!cm.isSortable(index));
60479         if(this.grid.enableColLock !== false){
60480             ms.get("lock").setDisabled(cm.isLocked(index));
60481             ms.get("unlock").setDisabled(!cm.isLocked(index));
60482         }
60483         this.hmenu.show(hd, "tl-bl");
60484     },
60485
60486     handleHdOver : function(e){
60487         var hd = this.findHeaderCell(e.getTarget());
60488         if(hd && !this.headersDisabled){
60489             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60490                this.fly(hd).addClass("x-grid-hd-over");
60491             }
60492         }
60493     },
60494
60495     handleHdOut : function(e){
60496         var hd = this.findHeaderCell(e.getTarget());
60497         if(hd){
60498             this.fly(hd).removeClass("x-grid-hd-over");
60499         }
60500     },
60501
60502     handleSplitDblClick : function(e, t){
60503         var i = this.getCellIndex(t);
60504         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60505             this.autoSizeColumn(i, true);
60506             this.layout();
60507         }
60508     },
60509
60510     render : function(){
60511
60512         var cm = this.cm;
60513         var colCount = cm.getColumnCount();
60514
60515         if(this.grid.monitorWindowResize === true){
60516             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60517         }
60518         var header = this.renderHeaders();
60519         var body = this.templates.body.apply({rows:""});
60520         var html = this.templates.master.apply({
60521             lockedBody: body,
60522             body: body,
60523             lockedHeader: header[0],
60524             header: header[1]
60525         });
60526
60527         //this.updateColumns();
60528
60529         this.grid.getGridEl().dom.innerHTML = html;
60530
60531         this.initElements();
60532         
60533         // a kludge to fix the random scolling effect in webkit
60534         this.el.on("scroll", function() {
60535             this.el.dom.scrollTop=0; // hopefully not recursive..
60536         },this);
60537
60538         this.scroller.on("scroll", this.handleScroll, this);
60539         this.lockedBody.on("mousewheel", this.handleWheel, this);
60540         this.mainBody.on("mousewheel", this.handleWheel, this);
60541
60542         this.mainHd.on("mouseover", this.handleHdOver, this);
60543         this.mainHd.on("mouseout", this.handleHdOut, this);
60544         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60545                 {delegate: "."+this.splitClass});
60546
60547         this.lockedHd.on("mouseover", this.handleHdOver, this);
60548         this.lockedHd.on("mouseout", this.handleHdOut, this);
60549         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60550                 {delegate: "."+this.splitClass});
60551
60552         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60553             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60554         }
60555
60556         this.updateSplitters();
60557
60558         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60559             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60560             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60561         }
60562
60563         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60564             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60565             this.hmenu.add(
60566                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60567                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60568             );
60569             if(this.grid.enableColLock !== false){
60570                 this.hmenu.add('-',
60571                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60572                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60573                 );
60574             }
60575             if (Roo.isTouch) {
60576                  this.hmenu.add('-',
60577                     {id:"wider", text: this.columnsWiderText},
60578                     {id:"narrow", text: this.columnsNarrowText }
60579                 );
60580                 
60581                  
60582             }
60583             
60584             if(this.grid.enableColumnHide !== false){
60585
60586                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60587                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60588                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60589
60590                 this.hmenu.add('-',
60591                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60592                 );
60593             }
60594             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60595
60596             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60597         }
60598
60599         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60600             this.dd = new Roo.grid.GridDragZone(this.grid, {
60601                 ddGroup : this.grid.ddGroup || 'GridDD'
60602             });
60603             
60604         }
60605
60606         /*
60607         for(var i = 0; i < colCount; i++){
60608             if(cm.isHidden(i)){
60609                 this.hideColumn(i);
60610             }
60611             if(cm.config[i].align){
60612                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60613                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60614             }
60615         }*/
60616         
60617         this.updateHeaderSortState();
60618
60619         this.beforeInitialResize();
60620         this.layout(true);
60621
60622         // two part rendering gives faster view to the user
60623         this.renderPhase2.defer(1, this);
60624     },
60625
60626     renderPhase2 : function(){
60627         // render the rows now
60628         this.refresh();
60629         if(this.grid.autoSizeColumns){
60630             this.autoSizeColumns();
60631         }
60632     },
60633
60634     beforeInitialResize : function(){
60635
60636     },
60637
60638     onColumnSplitterMoved : function(i, w){
60639         this.userResized = true;
60640         var cm = this.grid.colModel;
60641         cm.setColumnWidth(i, w, true);
60642         var cid = cm.getColumnId(i);
60643         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60644         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60645         this.updateSplitters();
60646         this.layout();
60647         this.grid.fireEvent("columnresize", i, w);
60648     },
60649
60650     syncRowHeights : function(startIndex, endIndex){
60651         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60652             startIndex = startIndex || 0;
60653             var mrows = this.getBodyTable().rows;
60654             var lrows = this.getLockedTable().rows;
60655             var len = mrows.length-1;
60656             endIndex = Math.min(endIndex || len, len);
60657             for(var i = startIndex; i <= endIndex; i++){
60658                 var m = mrows[i], l = lrows[i];
60659                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60660                 m.style.height = l.style.height = h + "px";
60661             }
60662         }
60663     },
60664
60665     layout : function(initialRender, is2ndPass)
60666     {
60667         var g = this.grid;
60668         var auto = g.autoHeight;
60669         var scrollOffset = 16;
60670         var c = g.getGridEl(), cm = this.cm,
60671                 expandCol = g.autoExpandColumn,
60672                 gv = this;
60673         //c.beginMeasure();
60674
60675         if(!c.dom.offsetWidth){ // display:none?
60676             if(initialRender){
60677                 this.lockedWrap.show();
60678                 this.mainWrap.show();
60679             }
60680             return;
60681         }
60682
60683         var hasLock = this.cm.isLocked(0);
60684
60685         var tbh = this.headerPanel.getHeight();
60686         var bbh = this.footerPanel.getHeight();
60687
60688         if(auto){
60689             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60690             var newHeight = ch + c.getBorderWidth("tb");
60691             if(g.maxHeight){
60692                 newHeight = Math.min(g.maxHeight, newHeight);
60693             }
60694             c.setHeight(newHeight);
60695         }
60696
60697         if(g.autoWidth){
60698             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60699         }
60700
60701         var s = this.scroller;
60702
60703         var csize = c.getSize(true);
60704
60705         this.el.setSize(csize.width, csize.height);
60706
60707         this.headerPanel.setWidth(csize.width);
60708         this.footerPanel.setWidth(csize.width);
60709
60710         var hdHeight = this.mainHd.getHeight();
60711         var vw = csize.width;
60712         var vh = csize.height - (tbh + bbh);
60713
60714         s.setSize(vw, vh);
60715
60716         var bt = this.getBodyTable();
60717         
60718         if(cm.getLockedCount() == cm.config.length){
60719             bt = this.getLockedTable();
60720         }
60721         
60722         var ltWidth = hasLock ?
60723                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60724
60725         var scrollHeight = bt.offsetHeight;
60726         var scrollWidth = ltWidth + bt.offsetWidth;
60727         var vscroll = false, hscroll = false;
60728
60729         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60730
60731         var lw = this.lockedWrap, mw = this.mainWrap;
60732         var lb = this.lockedBody, mb = this.mainBody;
60733
60734         setTimeout(function(){
60735             var t = s.dom.offsetTop;
60736             var w = s.dom.clientWidth,
60737                 h = s.dom.clientHeight;
60738
60739             lw.setTop(t);
60740             lw.setSize(ltWidth, h);
60741
60742             mw.setLeftTop(ltWidth, t);
60743             mw.setSize(w-ltWidth, h);
60744
60745             lb.setHeight(h-hdHeight);
60746             mb.setHeight(h-hdHeight);
60747
60748             if(is2ndPass !== true && !gv.userResized && expandCol){
60749                 // high speed resize without full column calculation
60750                 
60751                 var ci = cm.getIndexById(expandCol);
60752                 if (ci < 0) {
60753                     ci = cm.findColumnIndex(expandCol);
60754                 }
60755                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60756                 var expandId = cm.getColumnId(ci);
60757                 var  tw = cm.getTotalWidth(false);
60758                 var currentWidth = cm.getColumnWidth(ci);
60759                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60760                 if(currentWidth != cw){
60761                     cm.setColumnWidth(ci, cw, true);
60762                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60763                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60764                     gv.updateSplitters();
60765                     gv.layout(false, true);
60766                 }
60767             }
60768
60769             if(initialRender){
60770                 lw.show();
60771                 mw.show();
60772             }
60773             //c.endMeasure();
60774         }, 10);
60775     },
60776
60777     onWindowResize : function(){
60778         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60779             return;
60780         }
60781         this.layout();
60782     },
60783
60784     appendFooter : function(parentEl){
60785         return null;
60786     },
60787
60788     sortAscText : "Sort Ascending",
60789     sortDescText : "Sort Descending",
60790     lockText : "Lock Column",
60791     unlockText : "Unlock Column",
60792     columnsText : "Columns",
60793  
60794     columnsWiderText : "Wider",
60795     columnsNarrowText : "Thinner"
60796 });
60797
60798
60799 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60800     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60801     this.proxy.el.addClass('x-grid3-col-dd');
60802 };
60803
60804 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60805     handleMouseDown : function(e){
60806
60807     },
60808
60809     callHandleMouseDown : function(e){
60810         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60811     }
60812 });
60813 /*
60814  * Based on:
60815  * Ext JS Library 1.1.1
60816  * Copyright(c) 2006-2007, Ext JS, LLC.
60817  *
60818  * Originally Released Under LGPL - original licence link has changed is not relivant.
60819  *
60820  * Fork - LGPL
60821  * <script type="text/javascript">
60822  */
60823  /**
60824  * @extends Roo.dd.DDProxy
60825  * @class Roo.grid.SplitDragZone
60826  * Support for Column Header resizing
60827  * @constructor
60828  * @param {Object} config
60829  */
60830 // private
60831 // This is a support class used internally by the Grid components
60832 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60833     this.grid = grid;
60834     this.view = grid.getView();
60835     this.proxy = this.view.resizeProxy;
60836     Roo.grid.SplitDragZone.superclass.constructor.call(
60837         this,
60838         hd, // ID
60839         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60840         {  // CONFIG
60841             dragElId : Roo.id(this.proxy.dom),
60842             resizeFrame:false
60843         }
60844     );
60845     
60846     this.setHandleElId(Roo.id(hd));
60847     if (hd2 !== false) {
60848         this.setOuterHandleElId(Roo.id(hd2));
60849     }
60850     
60851     this.scroll = false;
60852 };
60853 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60854     fly: Roo.Element.fly,
60855
60856     b4StartDrag : function(x, y){
60857         this.view.headersDisabled = true;
60858         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60859                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60860         );
60861         this.proxy.setHeight(h);
60862         
60863         // for old system colWidth really stored the actual width?
60864         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60865         // which in reality did not work.. - it worked only for fixed sizes
60866         // for resizable we need to use actual sizes.
60867         var w = this.cm.getColumnWidth(this.cellIndex);
60868         if (!this.view.mainWrap) {
60869             // bootstrap.
60870             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60871         }
60872         
60873         
60874         
60875         // this was w-this.grid.minColumnWidth;
60876         // doesnt really make sense? - w = thie curren width or the rendered one?
60877         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60878         this.resetConstraints();
60879         this.setXConstraint(minw, 1000);
60880         this.setYConstraint(0, 0);
60881         this.minX = x - minw;
60882         this.maxX = x + 1000;
60883         this.startPos = x;
60884         if (!this.view.mainWrap) { // this is Bootstrap code..
60885             this.getDragEl().style.display='block';
60886         }
60887         
60888         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60889     },
60890
60891
60892     handleMouseDown : function(e){
60893         ev = Roo.EventObject.setEvent(e);
60894         var t = this.fly(ev.getTarget());
60895         if(t.hasClass("x-grid-split")){
60896             this.cellIndex = this.view.getCellIndex(t.dom);
60897             this.split = t.dom;
60898             this.cm = this.grid.colModel;
60899             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60900                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60901             }
60902         }
60903     },
60904
60905     endDrag : function(e){
60906         this.view.headersDisabled = false;
60907         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60908         var diff = endX - this.startPos;
60909         // 
60910         var w = this.cm.getColumnWidth(this.cellIndex);
60911         if (!this.view.mainWrap) {
60912             w = 0;
60913         }
60914         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60915     },
60916
60917     autoOffset : function(){
60918         this.setDelta(0,0);
60919     }
60920 });/*
60921  * Based on:
60922  * Ext JS Library 1.1.1
60923  * Copyright(c) 2006-2007, Ext JS, LLC.
60924  *
60925  * Originally Released Under LGPL - original licence link has changed is not relivant.
60926  *
60927  * Fork - LGPL
60928  * <script type="text/javascript">
60929  */
60930  
60931 // private
60932 // This is a support class used internally by the Grid components
60933 Roo.grid.GridDragZone = function(grid, config){
60934     this.view = grid.getView();
60935     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60936     if(this.view.lockedBody){
60937         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60938         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60939     }
60940     this.scroll = false;
60941     this.grid = grid;
60942     this.ddel = document.createElement('div');
60943     this.ddel.className = 'x-grid-dd-wrap';
60944 };
60945
60946 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60947     ddGroup : "GridDD",
60948
60949     getDragData : function(e){
60950         var t = Roo.lib.Event.getTarget(e);
60951         var rowIndex = this.view.findRowIndex(t);
60952         var sm = this.grid.selModel;
60953             
60954         //Roo.log(rowIndex);
60955         
60956         if (sm.getSelectedCell) {
60957             // cell selection..
60958             if (!sm.getSelectedCell()) {
60959                 return false;
60960             }
60961             if (rowIndex != sm.getSelectedCell()[0]) {
60962                 return false;
60963             }
60964         
60965         }
60966         if (sm.getSelections && sm.getSelections().length < 1) {
60967             return false;
60968         }
60969         
60970         
60971         // before it used to all dragging of unseleted... - now we dont do that.
60972         if(rowIndex !== false){
60973             
60974             // if editorgrid.. 
60975             
60976             
60977             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60978                
60979             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60980               //  
60981             //}
60982             if (e.hasModifier()){
60983                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60984             }
60985             
60986             Roo.log("getDragData");
60987             
60988             return {
60989                 grid: this.grid,
60990                 ddel: this.ddel,
60991                 rowIndex: rowIndex,
60992                 selections: sm.getSelections ? sm.getSelections() : (
60993                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60994             };
60995         }
60996         return false;
60997     },
60998     
60999     
61000     onInitDrag : function(e){
61001         var data = this.dragData;
61002         this.ddel.innerHTML = this.grid.getDragDropText();
61003         this.proxy.update(this.ddel);
61004         // fire start drag?
61005     },
61006
61007     afterRepair : function(){
61008         this.dragging = false;
61009     },
61010
61011     getRepairXY : function(e, data){
61012         return false;
61013     },
61014
61015     onEndDrag : function(data, e){
61016         // fire end drag?
61017     },
61018
61019     onValidDrop : function(dd, e, id){
61020         // fire drag drop?
61021         this.hideProxy();
61022     },
61023
61024     beforeInvalidDrop : function(e, id){
61025
61026     }
61027 });/*
61028  * Based on:
61029  * Ext JS Library 1.1.1
61030  * Copyright(c) 2006-2007, Ext JS, LLC.
61031  *
61032  * Originally Released Under LGPL - original licence link has changed is not relivant.
61033  *
61034  * Fork - LGPL
61035  * <script type="text/javascript">
61036  */
61037  
61038
61039 /**
61040  * @class Roo.grid.ColumnModel
61041  * @extends Roo.util.Observable
61042  * This is the default implementation of a ColumnModel used by the Grid. It defines
61043  * the columns in the grid.
61044  * <br>Usage:<br>
61045  <pre><code>
61046  var colModel = new Roo.grid.ColumnModel([
61047         {header: "Ticker", width: 60, sortable: true, locked: true},
61048         {header: "Company Name", width: 150, sortable: true},
61049         {header: "Market Cap.", width: 100, sortable: true},
61050         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61051         {header: "Employees", width: 100, sortable: true, resizable: false}
61052  ]);
61053  </code></pre>
61054  * <p>
61055  
61056  * The config options listed for this class are options which may appear in each
61057  * individual column definition.
61058  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61059  * @constructor
61060  * @param {Object} config An Array of column config objects. See this class's
61061  * config objects for details.
61062 */
61063 Roo.grid.ColumnModel = function(config){
61064         /**
61065      * The config passed into the constructor
61066      */
61067     this.config = []; //config;
61068     this.lookup = {};
61069
61070     // if no id, create one
61071     // if the column does not have a dataIndex mapping,
61072     // map it to the order it is in the config
61073     for(var i = 0, len = config.length; i < len; i++){
61074         this.addColumn(config[i]);
61075         
61076     }
61077
61078     /**
61079      * The width of columns which have no width specified (defaults to 100)
61080      * @type Number
61081      */
61082     this.defaultWidth = 100;
61083
61084     /**
61085      * Default sortable of columns which have no sortable specified (defaults to false)
61086      * @type Boolean
61087      */
61088     this.defaultSortable = false;
61089
61090     this.addEvents({
61091         /**
61092              * @event widthchange
61093              * Fires when the width of a column changes.
61094              * @param {ColumnModel} this
61095              * @param {Number} columnIndex The column index
61096              * @param {Number} newWidth The new width
61097              */
61098             "widthchange": true,
61099         /**
61100              * @event headerchange
61101              * Fires when the text of a header changes.
61102              * @param {ColumnModel} this
61103              * @param {Number} columnIndex The column index
61104              * @param {Number} newText The new header text
61105              */
61106             "headerchange": true,
61107         /**
61108              * @event hiddenchange
61109              * Fires when a column is hidden or "unhidden".
61110              * @param {ColumnModel} this
61111              * @param {Number} columnIndex The column index
61112              * @param {Boolean} hidden true if hidden, false otherwise
61113              */
61114             "hiddenchange": true,
61115             /**
61116          * @event columnmoved
61117          * Fires when a column is moved.
61118          * @param {ColumnModel} this
61119          * @param {Number} oldIndex
61120          * @param {Number} newIndex
61121          */
61122         "columnmoved" : true,
61123         /**
61124          * @event columlockchange
61125          * Fires when a column's locked state is changed
61126          * @param {ColumnModel} this
61127          * @param {Number} colIndex
61128          * @param {Boolean} locked true if locked
61129          */
61130         "columnlockchange" : true
61131     });
61132     Roo.grid.ColumnModel.superclass.constructor.call(this);
61133 };
61134 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61135     /**
61136      * @cfg {String} header The header text to display in the Grid view.
61137      */
61138         /**
61139      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61140      */
61141         /**
61142      * @cfg {String} smHeader Header at Bootsrap Small width
61143      */
61144         /**
61145      * @cfg {String} mdHeader Header at Bootsrap Medium width
61146      */
61147         /**
61148      * @cfg {String} lgHeader Header at Bootsrap Large width
61149      */
61150         /**
61151      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61152      */
61153     /**
61154      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61155      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61156      * specified, the column's index is used as an index into the Record's data Array.
61157      */
61158     /**
61159      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61160      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61161      */
61162     /**
61163      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61164      * Defaults to the value of the {@link #defaultSortable} property.
61165      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61166      */
61167     /**
61168      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61169      */
61170     /**
61171      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61172      */
61173     /**
61174      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61175      */
61176     /**
61177      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61178      */
61179     /**
61180      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61181      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61182      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61183      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61184      */
61185        /**
61186      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61187      */
61188     /**
61189      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61190      */
61191     /**
61192      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61193      */
61194     /**
61195      * @cfg {String} cursor (Optional)
61196      */
61197     /**
61198      * @cfg {String} tooltip (Optional)
61199      */
61200     /**
61201      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61202      */
61203     /**
61204      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61205      */
61206     /**
61207      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61208      */
61209     /**
61210      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61211      */
61212         /**
61213      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61214      */
61215     /**
61216      * Returns the id of the column at the specified index.
61217      * @param {Number} index The column index
61218      * @return {String} the id
61219      */
61220     getColumnId : function(index){
61221         return this.config[index].id;
61222     },
61223
61224     /**
61225      * Returns the column for a specified id.
61226      * @param {String} id The column id
61227      * @return {Object} the column
61228      */
61229     getColumnById : function(id){
61230         return this.lookup[id];
61231     },
61232
61233     
61234     /**
61235      * Returns the column Object for a specified dataIndex.
61236      * @param {String} dataIndex The column dataIndex
61237      * @return {Object|Boolean} the column or false if not found
61238      */
61239     getColumnByDataIndex: function(dataIndex){
61240         var index = this.findColumnIndex(dataIndex);
61241         return index > -1 ? this.config[index] : false;
61242     },
61243     
61244     /**
61245      * Returns the index for a specified column id.
61246      * @param {String} id The column id
61247      * @return {Number} the index, or -1 if not found
61248      */
61249     getIndexById : function(id){
61250         for(var i = 0, len = this.config.length; i < len; i++){
61251             if(this.config[i].id == id){
61252                 return i;
61253             }
61254         }
61255         return -1;
61256     },
61257     
61258     /**
61259      * Returns the index for a specified column dataIndex.
61260      * @param {String} dataIndex The column dataIndex
61261      * @return {Number} the index, or -1 if not found
61262      */
61263     
61264     findColumnIndex : function(dataIndex){
61265         for(var i = 0, len = this.config.length; i < len; i++){
61266             if(this.config[i].dataIndex == dataIndex){
61267                 return i;
61268             }
61269         }
61270         return -1;
61271     },
61272     
61273     
61274     moveColumn : function(oldIndex, newIndex){
61275         var c = this.config[oldIndex];
61276         this.config.splice(oldIndex, 1);
61277         this.config.splice(newIndex, 0, c);
61278         this.dataMap = null;
61279         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61280     },
61281
61282     isLocked : function(colIndex){
61283         return this.config[colIndex].locked === true;
61284     },
61285
61286     setLocked : function(colIndex, value, suppressEvent){
61287         if(this.isLocked(colIndex) == value){
61288             return;
61289         }
61290         this.config[colIndex].locked = value;
61291         if(!suppressEvent){
61292             this.fireEvent("columnlockchange", this, colIndex, value);
61293         }
61294     },
61295
61296     getTotalLockedWidth : function(){
61297         var totalWidth = 0;
61298         for(var i = 0; i < this.config.length; i++){
61299             if(this.isLocked(i) && !this.isHidden(i)){
61300                 this.totalWidth += this.getColumnWidth(i);
61301             }
61302         }
61303         return totalWidth;
61304     },
61305
61306     getLockedCount : function(){
61307         for(var i = 0, len = this.config.length; i < len; i++){
61308             if(!this.isLocked(i)){
61309                 return i;
61310             }
61311         }
61312         
61313         return this.config.length;
61314     },
61315
61316     /**
61317      * Returns the number of columns.
61318      * @return {Number}
61319      */
61320     getColumnCount : function(visibleOnly){
61321         if(visibleOnly === true){
61322             var c = 0;
61323             for(var i = 0, len = this.config.length; i < len; i++){
61324                 if(!this.isHidden(i)){
61325                     c++;
61326                 }
61327             }
61328             return c;
61329         }
61330         return this.config.length;
61331     },
61332
61333     /**
61334      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61335      * @param {Function} fn
61336      * @param {Object} scope (optional)
61337      * @return {Array} result
61338      */
61339     getColumnsBy : function(fn, scope){
61340         var r = [];
61341         for(var i = 0, len = this.config.length; i < len; i++){
61342             var c = this.config[i];
61343             if(fn.call(scope||this, c, i) === true){
61344                 r[r.length] = c;
61345             }
61346         }
61347         return r;
61348     },
61349
61350     /**
61351      * Returns true if the specified column is sortable.
61352      * @param {Number} col The column index
61353      * @return {Boolean}
61354      */
61355     isSortable : function(col){
61356         if(typeof this.config[col].sortable == "undefined"){
61357             return this.defaultSortable;
61358         }
61359         return this.config[col].sortable;
61360     },
61361
61362     /**
61363      * Returns the rendering (formatting) function defined for the column.
61364      * @param {Number} col The column index.
61365      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61366      */
61367     getRenderer : function(col){
61368         if(!this.config[col].renderer){
61369             return Roo.grid.ColumnModel.defaultRenderer;
61370         }
61371         return this.config[col].renderer;
61372     },
61373
61374     /**
61375      * Sets the rendering (formatting) function for a column.
61376      * @param {Number} col The column index
61377      * @param {Function} fn The function to use to process the cell's raw data
61378      * to return HTML markup for the grid view. The render function is called with
61379      * the following parameters:<ul>
61380      * <li>Data value.</li>
61381      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61382      * <li>css A CSS style string to apply to the table cell.</li>
61383      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61384      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61385      * <li>Row index</li>
61386      * <li>Column index</li>
61387      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61388      */
61389     setRenderer : function(col, fn){
61390         this.config[col].renderer = fn;
61391     },
61392
61393     /**
61394      * Returns the width for the specified column.
61395      * @param {Number} col The column index
61396      * @param (optional) {String} gridSize bootstrap width size.
61397      * @return {Number}
61398      */
61399     getColumnWidth : function(col, gridSize)
61400         {
61401                 var cfg = this.config[col];
61402                 
61403                 if (typeof(gridSize) == 'undefined') {
61404                         return cfg.width * 1 || this.defaultWidth;
61405                 }
61406                 if (gridSize === false) { // if we set it..
61407                         return cfg.width || false;
61408                 }
61409                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61410                 
61411                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61412                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61413                                 continue;
61414                         }
61415                         return cfg[ sizes[i] ];
61416                 }
61417                 return 1;
61418                 
61419     },
61420
61421     /**
61422      * Sets the width for a column.
61423      * @param {Number} col The column index
61424      * @param {Number} width The new width
61425      */
61426     setColumnWidth : function(col, width, suppressEvent){
61427         this.config[col].width = width;
61428         this.totalWidth = null;
61429         if(!suppressEvent){
61430              this.fireEvent("widthchange", this, col, width);
61431         }
61432     },
61433
61434     /**
61435      * Returns the total width of all columns.
61436      * @param {Boolean} includeHidden True to include hidden column widths
61437      * @return {Number}
61438      */
61439     getTotalWidth : function(includeHidden){
61440         if(!this.totalWidth){
61441             this.totalWidth = 0;
61442             for(var i = 0, len = this.config.length; i < len; i++){
61443                 if(includeHidden || !this.isHidden(i)){
61444                     this.totalWidth += this.getColumnWidth(i);
61445                 }
61446             }
61447         }
61448         return this.totalWidth;
61449     },
61450
61451     /**
61452      * Returns the header for the specified column.
61453      * @param {Number} col The column index
61454      * @return {String}
61455      */
61456     getColumnHeader : function(col){
61457         return this.config[col].header;
61458     },
61459
61460     /**
61461      * Sets the header for a column.
61462      * @param {Number} col The column index
61463      * @param {String} header The new header
61464      */
61465     setColumnHeader : function(col, header){
61466         this.config[col].header = header;
61467         this.fireEvent("headerchange", this, col, header);
61468     },
61469
61470     /**
61471      * Returns the tooltip for the specified column.
61472      * @param {Number} col The column index
61473      * @return {String}
61474      */
61475     getColumnTooltip : function(col){
61476             return this.config[col].tooltip;
61477     },
61478     /**
61479      * Sets the tooltip for a column.
61480      * @param {Number} col The column index
61481      * @param {String} tooltip The new tooltip
61482      */
61483     setColumnTooltip : function(col, tooltip){
61484             this.config[col].tooltip = tooltip;
61485     },
61486
61487     /**
61488      * Returns the dataIndex for the specified column.
61489      * @param {Number} col The column index
61490      * @return {Number}
61491      */
61492     getDataIndex : function(col){
61493         return this.config[col].dataIndex;
61494     },
61495
61496     /**
61497      * Sets the dataIndex for a column.
61498      * @param {Number} col The column index
61499      * @param {Number} dataIndex The new dataIndex
61500      */
61501     setDataIndex : function(col, dataIndex){
61502         this.config[col].dataIndex = dataIndex;
61503     },
61504
61505     
61506     
61507     /**
61508      * Returns true if the cell is editable.
61509      * @param {Number} colIndex The column index
61510      * @param {Number} rowIndex The row index - this is nto actually used..?
61511      * @return {Boolean}
61512      */
61513     isCellEditable : function(colIndex, rowIndex){
61514         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61515     },
61516
61517     /**
61518      * Returns the editor defined for the cell/column.
61519      * return false or null to disable editing.
61520      * @param {Number} colIndex The column index
61521      * @param {Number} rowIndex The row index
61522      * @return {Object}
61523      */
61524     getCellEditor : function(colIndex, rowIndex){
61525         return this.config[colIndex].editor;
61526     },
61527
61528     /**
61529      * Sets if a column is editable.
61530      * @param {Number} col The column index
61531      * @param {Boolean} editable True if the column is editable
61532      */
61533     setEditable : function(col, editable){
61534         this.config[col].editable = editable;
61535     },
61536
61537
61538     /**
61539      * Returns true if the column is hidden.
61540      * @param {Number} colIndex The column index
61541      * @return {Boolean}
61542      */
61543     isHidden : function(colIndex){
61544         return this.config[colIndex].hidden;
61545     },
61546
61547
61548     /**
61549      * Returns true if the column width cannot be changed
61550      */
61551     isFixed : function(colIndex){
61552         return this.config[colIndex].fixed;
61553     },
61554
61555     /**
61556      * Returns true if the column can be resized
61557      * @return {Boolean}
61558      */
61559     isResizable : function(colIndex){
61560         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61561     },
61562     /**
61563      * Sets if a column is hidden.
61564      * @param {Number} colIndex The column index
61565      * @param {Boolean} hidden True if the column is hidden
61566      */
61567     setHidden : function(colIndex, hidden){
61568         this.config[colIndex].hidden = hidden;
61569         this.totalWidth = null;
61570         this.fireEvent("hiddenchange", this, colIndex, hidden);
61571     },
61572
61573     /**
61574      * Sets the editor for a column.
61575      * @param {Number} col The column index
61576      * @param {Object} editor The editor object
61577      */
61578     setEditor : function(col, editor){
61579         this.config[col].editor = editor;
61580     },
61581     /**
61582      * Add a column (experimental...) - defaults to adding to the end..
61583      * @param {Object} config 
61584     */
61585     addColumn : function(c)
61586     {
61587     
61588         var i = this.config.length;
61589         this.config[i] = c;
61590         
61591         if(typeof c.dataIndex == "undefined"){
61592             c.dataIndex = i;
61593         }
61594         if(typeof c.renderer == "string"){
61595             c.renderer = Roo.util.Format[c.renderer];
61596         }
61597         if(typeof c.id == "undefined"){
61598             c.id = Roo.id();
61599         }
61600         if(c.editor && c.editor.xtype){
61601             c.editor  = Roo.factory(c.editor, Roo.grid);
61602         }
61603         if(c.editor && c.editor.isFormField){
61604             c.editor = new Roo.grid.GridEditor(c.editor);
61605         }
61606         this.lookup[c.id] = c;
61607     }
61608     
61609 });
61610
61611 Roo.grid.ColumnModel.defaultRenderer = function(value)
61612 {
61613     if(typeof value == "object") {
61614         return value;
61615     }
61616         if(typeof value == "string" && value.length < 1){
61617             return "&#160;";
61618         }
61619     
61620         return String.format("{0}", value);
61621 };
61622
61623 // Alias for backwards compatibility
61624 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61625 /*
61626  * Based on:
61627  * Ext JS Library 1.1.1
61628  * Copyright(c) 2006-2007, Ext JS, LLC.
61629  *
61630  * Originally Released Under LGPL - original licence link has changed is not relivant.
61631  *
61632  * Fork - LGPL
61633  * <script type="text/javascript">
61634  */
61635
61636 /**
61637  * @class Roo.grid.AbstractSelectionModel
61638  * @extends Roo.util.Observable
61639  * @abstract
61640  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61641  * implemented by descendant classes.  This class should not be directly instantiated.
61642  * @constructor
61643  */
61644 Roo.grid.AbstractSelectionModel = function(){
61645     this.locked = false;
61646     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61647 };
61648
61649 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61650     /** @ignore Called by the grid automatically. Do not call directly. */
61651     init : function(grid){
61652         this.grid = grid;
61653         this.initEvents();
61654     },
61655
61656     /**
61657      * Locks the selections.
61658      */
61659     lock : function(){
61660         this.locked = true;
61661     },
61662
61663     /**
61664      * Unlocks the selections.
61665      */
61666     unlock : function(){
61667         this.locked = false;
61668     },
61669
61670     /**
61671      * Returns true if the selections are locked.
61672      * @return {Boolean}
61673      */
61674     isLocked : function(){
61675         return this.locked;
61676     }
61677 });/*
61678  * Based on:
61679  * Ext JS Library 1.1.1
61680  * Copyright(c) 2006-2007, Ext JS, LLC.
61681  *
61682  * Originally Released Under LGPL - original licence link has changed is not relivant.
61683  *
61684  * Fork - LGPL
61685  * <script type="text/javascript">
61686  */
61687 /**
61688  * @extends Roo.grid.AbstractSelectionModel
61689  * @class Roo.grid.RowSelectionModel
61690  * The default SelectionModel used by {@link Roo.grid.Grid}.
61691  * It supports multiple selections and keyboard selection/navigation. 
61692  * @constructor
61693  * @param {Object} config
61694  */
61695 Roo.grid.RowSelectionModel = function(config){
61696     Roo.apply(this, config);
61697     this.selections = new Roo.util.MixedCollection(false, function(o){
61698         return o.id;
61699     });
61700
61701     this.last = false;
61702     this.lastActive = false;
61703
61704     this.addEvents({
61705         /**
61706         * @event selectionchange
61707         * Fires when the selection changes
61708         * @param {SelectionModel} this
61709         */
61710        "selectionchange" : true,
61711        /**
61712         * @event afterselectionchange
61713         * Fires after the selection changes (eg. by key press or clicking)
61714         * @param {SelectionModel} this
61715         */
61716        "afterselectionchange" : true,
61717        /**
61718         * @event beforerowselect
61719         * Fires when a row is selected being selected, return false to cancel.
61720         * @param {SelectionModel} this
61721         * @param {Number} rowIndex The selected index
61722         * @param {Boolean} keepExisting False if other selections will be cleared
61723         */
61724        "beforerowselect" : true,
61725        /**
61726         * @event rowselect
61727         * Fires when a row is selected.
61728         * @param {SelectionModel} this
61729         * @param {Number} rowIndex The selected index
61730         * @param {Roo.data.Record} r The record
61731         */
61732        "rowselect" : true,
61733        /**
61734         * @event rowdeselect
61735         * Fires when a row is deselected.
61736         * @param {SelectionModel} this
61737         * @param {Number} rowIndex The selected index
61738         */
61739         "rowdeselect" : true
61740     });
61741     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61742     this.locked = false;
61743 };
61744
61745 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61746     /**
61747      * @cfg {Boolean} singleSelect
61748      * True to allow selection of only one row at a time (defaults to false)
61749      */
61750     singleSelect : false,
61751
61752     // private
61753     initEvents : function(){
61754
61755         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61756             this.grid.on("mousedown", this.handleMouseDown, this);
61757         }else{ // allow click to work like normal
61758             this.grid.on("rowclick", this.handleDragableRowClick, this);
61759         }
61760         // bootstrap does not have a view..
61761         var view = this.grid.view ? this.grid.view : this.grid;
61762         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61763             "up" : function(e){
61764                 if(!e.shiftKey){
61765                     this.selectPrevious(e.shiftKey);
61766                 }else if(this.last !== false && this.lastActive !== false){
61767                     var last = this.last;
61768                     this.selectRange(this.last,  this.lastActive-1);
61769                     view.focusRow(this.lastActive);
61770                     if(last !== false){
61771                         this.last = last;
61772                     }
61773                 }else{
61774                     this.selectFirstRow();
61775                 }
61776                 this.fireEvent("afterselectionchange", this);
61777             },
61778             "down" : function(e){
61779                 if(!e.shiftKey){
61780                     this.selectNext(e.shiftKey);
61781                 }else if(this.last !== false && this.lastActive !== false){
61782                     var last = this.last;
61783                     this.selectRange(this.last,  this.lastActive+1);
61784                     view.focusRow(this.lastActive);
61785                     if(last !== false){
61786                         this.last = last;
61787                     }
61788                 }else{
61789                     this.selectFirstRow();
61790                 }
61791                 this.fireEvent("afterselectionchange", this);
61792             },
61793             scope: this
61794         });
61795
61796          
61797         view.on("refresh", this.onRefresh, this);
61798         view.on("rowupdated", this.onRowUpdated, this);
61799         view.on("rowremoved", this.onRemove, this);
61800     },
61801
61802     // private
61803     onRefresh : function(){
61804         var ds = this.grid.ds, i, v = this.grid.view;
61805         var s = this.selections;
61806         s.each(function(r){
61807             if((i = ds.indexOfId(r.id)) != -1){
61808                 v.onRowSelect(i);
61809                 s.add(ds.getAt(i)); // updating the selection relate data
61810             }else{
61811                 s.remove(r);
61812             }
61813         });
61814     },
61815
61816     // private
61817     onRemove : function(v, index, r){
61818         this.selections.remove(r);
61819     },
61820
61821     // private
61822     onRowUpdated : function(v, index, r){
61823         if(this.isSelected(r)){
61824             v.onRowSelect(index);
61825         }
61826     },
61827
61828     /**
61829      * Select records.
61830      * @param {Array} records The records to select
61831      * @param {Boolean} keepExisting (optional) True to keep existing selections
61832      */
61833     selectRecords : function(records, keepExisting){
61834         if(!keepExisting){
61835             this.clearSelections();
61836         }
61837         var ds = this.grid.ds;
61838         for(var i = 0, len = records.length; i < len; i++){
61839             this.selectRow(ds.indexOf(records[i]), true);
61840         }
61841     },
61842
61843     /**
61844      * Gets the number of selected rows.
61845      * @return {Number}
61846      */
61847     getCount : function(){
61848         return this.selections.length;
61849     },
61850
61851     /**
61852      * Selects the first row in the grid.
61853      */
61854     selectFirstRow : function(){
61855         this.selectRow(0);
61856     },
61857
61858     /**
61859      * Select the last row.
61860      * @param {Boolean} keepExisting (optional) True to keep existing selections
61861      */
61862     selectLastRow : function(keepExisting){
61863         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61864     },
61865
61866     /**
61867      * Selects the row immediately following the last selected row.
61868      * @param {Boolean} keepExisting (optional) True to keep existing selections
61869      */
61870     selectNext : function(keepExisting){
61871         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61872             this.selectRow(this.last+1, keepExisting);
61873             var view = this.grid.view ? this.grid.view : this.grid;
61874             view.focusRow(this.last);
61875         }
61876     },
61877
61878     /**
61879      * Selects the row that precedes the last selected row.
61880      * @param {Boolean} keepExisting (optional) True to keep existing selections
61881      */
61882     selectPrevious : function(keepExisting){
61883         if(this.last){
61884             this.selectRow(this.last-1, keepExisting);
61885             var view = this.grid.view ? this.grid.view : this.grid;
61886             view.focusRow(this.last);
61887         }
61888     },
61889
61890     /**
61891      * Returns the selected records
61892      * @return {Array} Array of selected records
61893      */
61894     getSelections : function(){
61895         return [].concat(this.selections.items);
61896     },
61897
61898     /**
61899      * Returns the first selected record.
61900      * @return {Record}
61901      */
61902     getSelected : function(){
61903         return this.selections.itemAt(0);
61904     },
61905
61906
61907     /**
61908      * Clears all selections.
61909      */
61910     clearSelections : function(fast){
61911         if(this.locked) {
61912             return;
61913         }
61914         if(fast !== true){
61915             var ds = this.grid.ds;
61916             var s = this.selections;
61917             s.each(function(r){
61918                 this.deselectRow(ds.indexOfId(r.id));
61919             }, this);
61920             s.clear();
61921         }else{
61922             this.selections.clear();
61923         }
61924         this.last = false;
61925     },
61926
61927
61928     /**
61929      * Selects all rows.
61930      */
61931     selectAll : function(){
61932         if(this.locked) {
61933             return;
61934         }
61935         this.selections.clear();
61936         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61937             this.selectRow(i, true);
61938         }
61939     },
61940
61941     /**
61942      * Returns True if there is a selection.
61943      * @return {Boolean}
61944      */
61945     hasSelection : function(){
61946         return this.selections.length > 0;
61947     },
61948
61949     /**
61950      * Returns True if the specified row is selected.
61951      * @param {Number/Record} record The record or index of the record to check
61952      * @return {Boolean}
61953      */
61954     isSelected : function(index){
61955         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61956         return (r && this.selections.key(r.id) ? true : false);
61957     },
61958
61959     /**
61960      * Returns True if the specified record id is selected.
61961      * @param {String} id The id of record to check
61962      * @return {Boolean}
61963      */
61964     isIdSelected : function(id){
61965         return (this.selections.key(id) ? true : false);
61966     },
61967
61968     // private
61969     handleMouseDown : function(e, t)
61970     {
61971         var view = this.grid.view ? this.grid.view : this.grid;
61972         var rowIndex;
61973         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61974             return;
61975         };
61976         if(e.shiftKey && this.last !== false){
61977             var last = this.last;
61978             this.selectRange(last, rowIndex, e.ctrlKey);
61979             this.last = last; // reset the last
61980             view.focusRow(rowIndex);
61981         }else{
61982             var isSelected = this.isSelected(rowIndex);
61983             if(e.button !== 0 && isSelected){
61984                 view.focusRow(rowIndex);
61985             }else if(e.ctrlKey && isSelected){
61986                 this.deselectRow(rowIndex);
61987             }else if(!isSelected){
61988                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61989                 view.focusRow(rowIndex);
61990             }
61991         }
61992         this.fireEvent("afterselectionchange", this);
61993     },
61994     // private
61995     handleDragableRowClick :  function(grid, rowIndex, e) 
61996     {
61997         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61998             this.selectRow(rowIndex, false);
61999             var view = this.grid.view ? this.grid.view : this.grid;
62000             view.focusRow(rowIndex);
62001              this.fireEvent("afterselectionchange", this);
62002         }
62003     },
62004     
62005     /**
62006      * Selects multiple rows.
62007      * @param {Array} rows Array of the indexes of the row to select
62008      * @param {Boolean} keepExisting (optional) True to keep existing selections
62009      */
62010     selectRows : function(rows, keepExisting){
62011         if(!keepExisting){
62012             this.clearSelections();
62013         }
62014         for(var i = 0, len = rows.length; i < len; i++){
62015             this.selectRow(rows[i], true);
62016         }
62017     },
62018
62019     /**
62020      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62021      * @param {Number} startRow The index of the first row in the range
62022      * @param {Number} endRow The index of the last row in the range
62023      * @param {Boolean} keepExisting (optional) True to retain existing selections
62024      */
62025     selectRange : function(startRow, endRow, keepExisting){
62026         if(this.locked) {
62027             return;
62028         }
62029         if(!keepExisting){
62030             this.clearSelections();
62031         }
62032         if(startRow <= endRow){
62033             for(var i = startRow; i <= endRow; i++){
62034                 this.selectRow(i, true);
62035             }
62036         }else{
62037             for(var i = startRow; i >= endRow; i--){
62038                 this.selectRow(i, true);
62039             }
62040         }
62041     },
62042
62043     /**
62044      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62045      * @param {Number} startRow The index of the first row in the range
62046      * @param {Number} endRow The index of the last row in the range
62047      */
62048     deselectRange : function(startRow, endRow, preventViewNotify){
62049         if(this.locked) {
62050             return;
62051         }
62052         for(var i = startRow; i <= endRow; i++){
62053             this.deselectRow(i, preventViewNotify);
62054         }
62055     },
62056
62057     /**
62058      * Selects a row.
62059      * @param {Number} row The index of the row to select
62060      * @param {Boolean} keepExisting (optional) True to keep existing selections
62061      */
62062     selectRow : function(index, keepExisting, preventViewNotify){
62063         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62064             return;
62065         }
62066         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62067             if(!keepExisting || this.singleSelect){
62068                 this.clearSelections();
62069             }
62070             var r = this.grid.ds.getAt(index);
62071             this.selections.add(r);
62072             this.last = this.lastActive = index;
62073             if(!preventViewNotify){
62074                 var view = this.grid.view ? this.grid.view : this.grid;
62075                 view.onRowSelect(index);
62076             }
62077             this.fireEvent("rowselect", this, index, r);
62078             this.fireEvent("selectionchange", this);
62079         }
62080     },
62081
62082     /**
62083      * Deselects a row.
62084      * @param {Number} row The index of the row to deselect
62085      */
62086     deselectRow : function(index, preventViewNotify){
62087         if(this.locked) {
62088             return;
62089         }
62090         if(this.last == index){
62091             this.last = false;
62092         }
62093         if(this.lastActive == index){
62094             this.lastActive = false;
62095         }
62096         var r = this.grid.ds.getAt(index);
62097         this.selections.remove(r);
62098         if(!preventViewNotify){
62099             var view = this.grid.view ? this.grid.view : this.grid;
62100             view.onRowDeselect(index);
62101         }
62102         this.fireEvent("rowdeselect", this, index);
62103         this.fireEvent("selectionchange", this);
62104     },
62105
62106     // private
62107     restoreLast : function(){
62108         if(this._last){
62109             this.last = this._last;
62110         }
62111     },
62112
62113     // private
62114     acceptsNav : function(row, col, cm){
62115         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62116     },
62117
62118     // private
62119     onEditorKey : function(field, e){
62120         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62121         if(k == e.TAB){
62122             e.stopEvent();
62123             ed.completeEdit();
62124             if(e.shiftKey){
62125                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62126             }else{
62127                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62128             }
62129         }else if(k == e.ENTER && !e.ctrlKey){
62130             e.stopEvent();
62131             ed.completeEdit();
62132             if(e.shiftKey){
62133                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62134             }else{
62135                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62136             }
62137         }else if(k == e.ESC){
62138             ed.cancelEdit();
62139         }
62140         if(newCell){
62141             g.startEditing(newCell[0], newCell[1]);
62142         }
62143     }
62144 });/*
62145  * Based on:
62146  * Ext JS Library 1.1.1
62147  * Copyright(c) 2006-2007, Ext JS, LLC.
62148  *
62149  * Originally Released Under LGPL - original licence link has changed is not relivant.
62150  *
62151  * Fork - LGPL
62152  * <script type="text/javascript">
62153  */
62154 /**
62155  * @class Roo.grid.CellSelectionModel
62156  * @extends Roo.grid.AbstractSelectionModel
62157  * This class provides the basic implementation for cell selection in a grid.
62158  * @constructor
62159  * @param {Object} config The object containing the configuration of this model.
62160  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62161  */
62162 Roo.grid.CellSelectionModel = function(config){
62163     Roo.apply(this, config);
62164
62165     this.selection = null;
62166
62167     this.addEvents({
62168         /**
62169              * @event beforerowselect
62170              * Fires before a cell is selected.
62171              * @param {SelectionModel} this
62172              * @param {Number} rowIndex The selected row index
62173              * @param {Number} colIndex The selected cell index
62174              */
62175             "beforecellselect" : true,
62176         /**
62177              * @event cellselect
62178              * Fires when a cell is selected.
62179              * @param {SelectionModel} this
62180              * @param {Number} rowIndex The selected row index
62181              * @param {Number} colIndex The selected cell index
62182              */
62183             "cellselect" : true,
62184         /**
62185              * @event selectionchange
62186              * Fires when the active selection changes.
62187              * @param {SelectionModel} this
62188              * @param {Object} selection null for no selection or an object (o) with two properties
62189                 <ul>
62190                 <li>o.record: the record object for the row the selection is in</li>
62191                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62192                 </ul>
62193              */
62194             "selectionchange" : true,
62195         /**
62196              * @event tabend
62197              * Fires when the tab (or enter) was pressed on the last editable cell
62198              * You can use this to trigger add new row.
62199              * @param {SelectionModel} this
62200              */
62201             "tabend" : true,
62202          /**
62203              * @event beforeeditnext
62204              * Fires before the next editable sell is made active
62205              * You can use this to skip to another cell or fire the tabend
62206              *    if you set cell to false
62207              * @param {Object} eventdata object : { cell : [ row, col ] } 
62208              */
62209             "beforeeditnext" : true
62210     });
62211     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62212 };
62213
62214 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62215     
62216     enter_is_tab: false,
62217
62218     /** @ignore */
62219     initEvents : function(){
62220         this.grid.on("mousedown", this.handleMouseDown, this);
62221         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62222         var view = this.grid.view;
62223         view.on("refresh", this.onViewChange, this);
62224         view.on("rowupdated", this.onRowUpdated, this);
62225         view.on("beforerowremoved", this.clearSelections, this);
62226         view.on("beforerowsinserted", this.clearSelections, this);
62227         if(this.grid.isEditor){
62228             this.grid.on("beforeedit", this.beforeEdit,  this);
62229         }
62230     },
62231
62232         //private
62233     beforeEdit : function(e){
62234         this.select(e.row, e.column, false, true, e.record);
62235     },
62236
62237         //private
62238     onRowUpdated : function(v, index, r){
62239         if(this.selection && this.selection.record == r){
62240             v.onCellSelect(index, this.selection.cell[1]);
62241         }
62242     },
62243
62244         //private
62245     onViewChange : function(){
62246         this.clearSelections(true);
62247     },
62248
62249         /**
62250          * Returns the currently selected cell,.
62251          * @return {Array} The selected cell (row, column) or null if none selected.
62252          */
62253     getSelectedCell : function(){
62254         return this.selection ? this.selection.cell : null;
62255     },
62256
62257     /**
62258      * Clears all selections.
62259      * @param {Boolean} true to prevent the gridview from being notified about the change.
62260      */
62261     clearSelections : function(preventNotify){
62262         var s = this.selection;
62263         if(s){
62264             if(preventNotify !== true){
62265                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62266             }
62267             this.selection = null;
62268             this.fireEvent("selectionchange", this, null);
62269         }
62270     },
62271
62272     /**
62273      * Returns true if there is a selection.
62274      * @return {Boolean}
62275      */
62276     hasSelection : function(){
62277         return this.selection ? true : false;
62278     },
62279
62280     /** @ignore */
62281     handleMouseDown : function(e, t){
62282         var v = this.grid.getView();
62283         if(this.isLocked()){
62284             return;
62285         };
62286         var row = v.findRowIndex(t);
62287         var cell = v.findCellIndex(t);
62288         if(row !== false && cell !== false){
62289             this.select(row, cell);
62290         }
62291     },
62292
62293     /**
62294      * Selects a cell.
62295      * @param {Number} rowIndex
62296      * @param {Number} collIndex
62297      */
62298     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62299         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62300             this.clearSelections();
62301             r = r || this.grid.dataSource.getAt(rowIndex);
62302             this.selection = {
62303                 record : r,
62304                 cell : [rowIndex, colIndex]
62305             };
62306             if(!preventViewNotify){
62307                 var v = this.grid.getView();
62308                 v.onCellSelect(rowIndex, colIndex);
62309                 if(preventFocus !== true){
62310                     v.focusCell(rowIndex, colIndex);
62311                 }
62312             }
62313             this.fireEvent("cellselect", this, rowIndex, colIndex);
62314             this.fireEvent("selectionchange", this, this.selection);
62315         }
62316     },
62317
62318         //private
62319     isSelectable : function(rowIndex, colIndex, cm){
62320         return !cm.isHidden(colIndex);
62321     },
62322
62323     /** @ignore */
62324     handleKeyDown : function(e){
62325         //Roo.log('Cell Sel Model handleKeyDown');
62326         if(!e.isNavKeyPress()){
62327             return;
62328         }
62329         var g = this.grid, s = this.selection;
62330         if(!s){
62331             e.stopEvent();
62332             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62333             if(cell){
62334                 this.select(cell[0], cell[1]);
62335             }
62336             return;
62337         }
62338         var sm = this;
62339         var walk = function(row, col, step){
62340             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62341         };
62342         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62343         var newCell;
62344
62345       
62346
62347         switch(k){
62348             case e.TAB:
62349                 // handled by onEditorKey
62350                 if (g.isEditor && g.editing) {
62351                     return;
62352                 }
62353                 if(e.shiftKey) {
62354                     newCell = walk(r, c-1, -1);
62355                 } else {
62356                     newCell = walk(r, c+1, 1);
62357                 }
62358                 break;
62359             
62360             case e.DOWN:
62361                newCell = walk(r+1, c, 1);
62362                 break;
62363             
62364             case e.UP:
62365                 newCell = walk(r-1, c, -1);
62366                 break;
62367             
62368             case e.RIGHT:
62369                 newCell = walk(r, c+1, 1);
62370                 break;
62371             
62372             case e.LEFT:
62373                 newCell = walk(r, c-1, -1);
62374                 break;
62375             
62376             case e.ENTER:
62377                 
62378                 if(g.isEditor && !g.editing){
62379                    g.startEditing(r, c);
62380                    e.stopEvent();
62381                    return;
62382                 }
62383                 
62384                 
62385              break;
62386         };
62387         if(newCell){
62388             this.select(newCell[0], newCell[1]);
62389             e.stopEvent();
62390             
62391         }
62392     },
62393
62394     acceptsNav : function(row, col, cm){
62395         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62396     },
62397     /**
62398      * Selects a cell.
62399      * @param {Number} field (not used) - as it's normally used as a listener
62400      * @param {Number} e - event - fake it by using
62401      *
62402      * var e = Roo.EventObjectImpl.prototype;
62403      * e.keyCode = e.TAB
62404      *
62405      * 
62406      */
62407     onEditorKey : function(field, e){
62408         
62409         var k = e.getKey(),
62410             newCell,
62411             g = this.grid,
62412             ed = g.activeEditor,
62413             forward = false;
62414         ///Roo.log('onEditorKey' + k);
62415         
62416         
62417         if (this.enter_is_tab && k == e.ENTER) {
62418             k = e.TAB;
62419         }
62420         
62421         if(k == e.TAB){
62422             if(e.shiftKey){
62423                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62424             }else{
62425                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62426                 forward = true;
62427             }
62428             
62429             e.stopEvent();
62430             
62431         } else if(k == e.ENTER &&  !e.ctrlKey){
62432             ed.completeEdit();
62433             e.stopEvent();
62434             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62435         
62436                 } else if(k == e.ESC){
62437             ed.cancelEdit();
62438         }
62439                 
62440         if (newCell) {
62441             var ecall = { cell : newCell, forward : forward };
62442             this.fireEvent('beforeeditnext', ecall );
62443             newCell = ecall.cell;
62444                         forward = ecall.forward;
62445         }
62446                 
62447         if(newCell){
62448             //Roo.log('next cell after edit');
62449             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62450         } else if (forward) {
62451             // tabbed past last
62452             this.fireEvent.defer(100, this, ['tabend',this]);
62453         }
62454     }
62455 });/*
62456  * Based on:
62457  * Ext JS Library 1.1.1
62458  * Copyright(c) 2006-2007, Ext JS, LLC.
62459  *
62460  * Originally Released Under LGPL - original licence link has changed is not relivant.
62461  *
62462  * Fork - LGPL
62463  * <script type="text/javascript">
62464  */
62465  
62466 /**
62467  * @class Roo.grid.EditorGrid
62468  * @extends Roo.grid.Grid
62469  * Class for creating and editable grid.
62470  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62471  * The container MUST have some type of size defined for the grid to fill. The container will be 
62472  * automatically set to position relative if it isn't already.
62473  * @param {Object} dataSource The data model to bind to
62474  * @param {Object} colModel The column model with info about this grid's columns
62475  */
62476 Roo.grid.EditorGrid = function(container, config){
62477     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62478     this.getGridEl().addClass("xedit-grid");
62479
62480     if(!this.selModel){
62481         this.selModel = new Roo.grid.CellSelectionModel();
62482     }
62483
62484     this.activeEditor = null;
62485
62486         this.addEvents({
62487             /**
62488              * @event beforeedit
62489              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62490              * <ul style="padding:5px;padding-left:16px;">
62491              * <li>grid - This grid</li>
62492              * <li>record - The record being edited</li>
62493              * <li>field - The field name being edited</li>
62494              * <li>value - The value for the field being edited.</li>
62495              * <li>row - The grid row index</li>
62496              * <li>column - The grid column index</li>
62497              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62498              * </ul>
62499              * @param {Object} e An edit event (see above for description)
62500              */
62501             "beforeedit" : true,
62502             /**
62503              * @event afteredit
62504              * Fires after a cell is edited. <br />
62505              * <ul style="padding:5px;padding-left:16px;">
62506              * <li>grid - This grid</li>
62507              * <li>record - The record being edited</li>
62508              * <li>field - The field name being edited</li>
62509              * <li>value - The value being set</li>
62510              * <li>originalValue - The original value for the field, before the edit.</li>
62511              * <li>row - The grid row index</li>
62512              * <li>column - The grid column index</li>
62513              * </ul>
62514              * @param {Object} e An edit event (see above for description)
62515              */
62516             "afteredit" : true,
62517             /**
62518              * @event validateedit
62519              * Fires after a cell is edited, but before the value is set in the record. 
62520          * You can use this to modify the value being set in the field, Return false
62521              * to cancel the change. The edit event object has the following properties <br />
62522              * <ul style="padding:5px;padding-left:16px;">
62523          * <li>editor - This editor</li>
62524              * <li>grid - This grid</li>
62525              * <li>record - The record being edited</li>
62526              * <li>field - The field name being edited</li>
62527              * <li>value - The value being set</li>
62528              * <li>originalValue - The original value for the field, before the edit.</li>
62529              * <li>row - The grid row index</li>
62530              * <li>column - The grid column index</li>
62531              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62532              * </ul>
62533              * @param {Object} e An edit event (see above for description)
62534              */
62535             "validateedit" : true
62536         });
62537     this.on("bodyscroll", this.stopEditing,  this);
62538     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62539 };
62540
62541 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62542     /**
62543      * @cfg {Number} clicksToEdit
62544      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62545      */
62546     clicksToEdit: 2,
62547
62548     // private
62549     isEditor : true,
62550     // private
62551     trackMouseOver: false, // causes very odd FF errors
62552
62553     onCellDblClick : function(g, row, col){
62554         this.startEditing(row, col);
62555     },
62556
62557     onEditComplete : function(ed, value, startValue){
62558         this.editing = false;
62559         this.activeEditor = null;
62560         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62561         var r = ed.record;
62562         var field = this.colModel.getDataIndex(ed.col);
62563         var e = {
62564             grid: this,
62565             record: r,
62566             field: field,
62567             originalValue: startValue,
62568             value: value,
62569             row: ed.row,
62570             column: ed.col,
62571             cancel:false,
62572             editor: ed
62573         };
62574         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62575         cell.show();
62576           
62577         if(String(value) !== String(startValue)){
62578             
62579             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62580                 r.set(field, e.value);
62581                 // if we are dealing with a combo box..
62582                 // then we also set the 'name' colum to be the displayField
62583                 if (ed.field.displayField && ed.field.name) {
62584                     r.set(ed.field.name, ed.field.el.dom.value);
62585                 }
62586                 
62587                 delete e.cancel; //?? why!!!
62588                 this.fireEvent("afteredit", e);
62589             }
62590         } else {
62591             this.fireEvent("afteredit", e); // always fire it!
62592         }
62593         this.view.focusCell(ed.row, ed.col);
62594     },
62595
62596     /**
62597      * Starts editing the specified for the specified row/column
62598      * @param {Number} rowIndex
62599      * @param {Number} colIndex
62600      */
62601     startEditing : function(row, col){
62602         this.stopEditing();
62603         if(this.colModel.isCellEditable(col, row)){
62604             this.view.ensureVisible(row, col, true);
62605           
62606             var r = this.dataSource.getAt(row);
62607             var field = this.colModel.getDataIndex(col);
62608             var cell = Roo.get(this.view.getCell(row,col));
62609             var e = {
62610                 grid: this,
62611                 record: r,
62612                 field: field,
62613                 value: r.data[field],
62614                 row: row,
62615                 column: col,
62616                 cancel:false 
62617             };
62618             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62619                 this.editing = true;
62620                 var ed = this.colModel.getCellEditor(col, row);
62621                 
62622                 if (!ed) {
62623                     return;
62624                 }
62625                 if(!ed.rendered){
62626                     ed.render(ed.parentEl || document.body);
62627                 }
62628                 ed.field.reset();
62629                
62630                 cell.hide();
62631                 
62632                 (function(){ // complex but required for focus issues in safari, ie and opera
62633                     ed.row = row;
62634                     ed.col = col;
62635                     ed.record = r;
62636                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62637                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62638                     this.activeEditor = ed;
62639                     var v = r.data[field];
62640                     ed.startEdit(this.view.getCell(row, col), v);
62641                     // combo's with 'displayField and name set
62642                     if (ed.field.displayField && ed.field.name) {
62643                         ed.field.el.dom.value = r.data[ed.field.name];
62644                     }
62645                     
62646                     
62647                 }).defer(50, this);
62648             }
62649         }
62650     },
62651         
62652     /**
62653      * Stops any active editing
62654      */
62655     stopEditing : function(){
62656         if(this.activeEditor){
62657             this.activeEditor.completeEdit();
62658         }
62659         this.activeEditor = null;
62660     },
62661         
62662          /**
62663      * Called to get grid's drag proxy text, by default returns this.ddText.
62664      * @return {String}
62665      */
62666     getDragDropText : function(){
62667         var count = this.selModel.getSelectedCell() ? 1 : 0;
62668         return String.format(this.ddText, count, count == 1 ? '' : 's');
62669     }
62670         
62671 });/*
62672  * Based on:
62673  * Ext JS Library 1.1.1
62674  * Copyright(c) 2006-2007, Ext JS, LLC.
62675  *
62676  * Originally Released Under LGPL - original licence link has changed is not relivant.
62677  *
62678  * Fork - LGPL
62679  * <script type="text/javascript">
62680  */
62681
62682 // private - not really -- you end up using it !
62683 // This is a support class used internally by the Grid components
62684
62685 /**
62686  * @class Roo.grid.GridEditor
62687  * @extends Roo.Editor
62688  * Class for creating and editable grid elements.
62689  * @param {Object} config any settings (must include field)
62690  */
62691 Roo.grid.GridEditor = function(field, config){
62692     if (!config && field.field) {
62693         config = field;
62694         field = Roo.factory(config.field, Roo.form);
62695     }
62696     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62697     field.monitorTab = false;
62698 };
62699
62700 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62701     
62702     /**
62703      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62704      */
62705     
62706     alignment: "tl-tl",
62707     autoSize: "width",
62708     hideEl : false,
62709     cls: "x-small-editor x-grid-editor",
62710     shim:false,
62711     shadow:"frame"
62712 });/*
62713  * Based on:
62714  * Ext JS Library 1.1.1
62715  * Copyright(c) 2006-2007, Ext JS, LLC.
62716  *
62717  * Originally Released Under LGPL - original licence link has changed is not relivant.
62718  *
62719  * Fork - LGPL
62720  * <script type="text/javascript">
62721  */
62722   
62723
62724   
62725 Roo.grid.PropertyRecord = Roo.data.Record.create([
62726     {name:'name',type:'string'},  'value'
62727 ]);
62728
62729
62730 Roo.grid.PropertyStore = function(grid, source){
62731     this.grid = grid;
62732     this.store = new Roo.data.Store({
62733         recordType : Roo.grid.PropertyRecord
62734     });
62735     this.store.on('update', this.onUpdate,  this);
62736     if(source){
62737         this.setSource(source);
62738     }
62739     Roo.grid.PropertyStore.superclass.constructor.call(this);
62740 };
62741
62742
62743
62744 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62745     setSource : function(o){
62746         this.source = o;
62747         this.store.removeAll();
62748         var data = [];
62749         for(var k in o){
62750             if(this.isEditableValue(o[k])){
62751                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62752             }
62753         }
62754         this.store.loadRecords({records: data}, {}, true);
62755     },
62756
62757     onUpdate : function(ds, record, type){
62758         if(type == Roo.data.Record.EDIT){
62759             var v = record.data['value'];
62760             var oldValue = record.modified['value'];
62761             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62762                 this.source[record.id] = v;
62763                 record.commit();
62764                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62765             }else{
62766                 record.reject();
62767             }
62768         }
62769     },
62770
62771     getProperty : function(row){
62772        return this.store.getAt(row);
62773     },
62774
62775     isEditableValue: function(val){
62776         if(val && val instanceof Date){
62777             return true;
62778         }else if(typeof val == 'object' || typeof val == 'function'){
62779             return false;
62780         }
62781         return true;
62782     },
62783
62784     setValue : function(prop, value){
62785         this.source[prop] = value;
62786         this.store.getById(prop).set('value', value);
62787     },
62788
62789     getSource : function(){
62790         return this.source;
62791     }
62792 });
62793
62794 Roo.grid.PropertyColumnModel = function(grid, store){
62795     this.grid = grid;
62796     var g = Roo.grid;
62797     g.PropertyColumnModel.superclass.constructor.call(this, [
62798         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62799         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62800     ]);
62801     this.store = store;
62802     this.bselect = Roo.DomHelper.append(document.body, {
62803         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62804             {tag: 'option', value: 'true', html: 'true'},
62805             {tag: 'option', value: 'false', html: 'false'}
62806         ]
62807     });
62808     Roo.id(this.bselect);
62809     var f = Roo.form;
62810     this.editors = {
62811         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62812         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62813         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62814         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62815         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62816     };
62817     this.renderCellDelegate = this.renderCell.createDelegate(this);
62818     this.renderPropDelegate = this.renderProp.createDelegate(this);
62819 };
62820
62821 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62822     
62823     
62824     nameText : 'Name',
62825     valueText : 'Value',
62826     
62827     dateFormat : 'm/j/Y',
62828     
62829     
62830     renderDate : function(dateVal){
62831         return dateVal.dateFormat(this.dateFormat);
62832     },
62833
62834     renderBool : function(bVal){
62835         return bVal ? 'true' : 'false';
62836     },
62837
62838     isCellEditable : function(colIndex, rowIndex){
62839         return colIndex == 1;
62840     },
62841
62842     getRenderer : function(col){
62843         return col == 1 ?
62844             this.renderCellDelegate : this.renderPropDelegate;
62845     },
62846
62847     renderProp : function(v){
62848         return this.getPropertyName(v);
62849     },
62850
62851     renderCell : function(val){
62852         var rv = val;
62853         if(val instanceof Date){
62854             rv = this.renderDate(val);
62855         }else if(typeof val == 'boolean'){
62856             rv = this.renderBool(val);
62857         }
62858         return Roo.util.Format.htmlEncode(rv);
62859     },
62860
62861     getPropertyName : function(name){
62862         var pn = this.grid.propertyNames;
62863         return pn && pn[name] ? pn[name] : name;
62864     },
62865
62866     getCellEditor : function(colIndex, rowIndex){
62867         var p = this.store.getProperty(rowIndex);
62868         var n = p.data['name'], val = p.data['value'];
62869         
62870         if(typeof(this.grid.customEditors[n]) == 'string'){
62871             return this.editors[this.grid.customEditors[n]];
62872         }
62873         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62874             return this.grid.customEditors[n];
62875         }
62876         if(val instanceof Date){
62877             return this.editors['date'];
62878         }else if(typeof val == 'number'){
62879             return this.editors['number'];
62880         }else if(typeof val == 'boolean'){
62881             return this.editors['boolean'];
62882         }else{
62883             return this.editors['string'];
62884         }
62885     }
62886 });
62887
62888 /**
62889  * @class Roo.grid.PropertyGrid
62890  * @extends Roo.grid.EditorGrid
62891  * This class represents the  interface of a component based property grid control.
62892  * <br><br>Usage:<pre><code>
62893  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62894       
62895  });
62896  // set any options
62897  grid.render();
62898  * </code></pre>
62899   
62900  * @constructor
62901  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62902  * The container MUST have some type of size defined for the grid to fill. The container will be
62903  * automatically set to position relative if it isn't already.
62904  * @param {Object} config A config object that sets properties on this grid.
62905  */
62906 Roo.grid.PropertyGrid = function(container, config){
62907     config = config || {};
62908     var store = new Roo.grid.PropertyStore(this);
62909     this.store = store;
62910     var cm = new Roo.grid.PropertyColumnModel(this, store);
62911     store.store.sort('name', 'ASC');
62912     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62913         ds: store.store,
62914         cm: cm,
62915         enableColLock:false,
62916         enableColumnMove:false,
62917         stripeRows:false,
62918         trackMouseOver: false,
62919         clicksToEdit:1
62920     }, config));
62921     this.getGridEl().addClass('x-props-grid');
62922     this.lastEditRow = null;
62923     this.on('columnresize', this.onColumnResize, this);
62924     this.addEvents({
62925          /**
62926              * @event beforepropertychange
62927              * Fires before a property changes (return false to stop?)
62928              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62929              * @param {String} id Record Id
62930              * @param {String} newval New Value
62931          * @param {String} oldval Old Value
62932              */
62933         "beforepropertychange": true,
62934         /**
62935              * @event propertychange
62936              * Fires after a property changes
62937              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62938              * @param {String} id Record Id
62939              * @param {String} newval New Value
62940          * @param {String} oldval Old Value
62941              */
62942         "propertychange": true
62943     });
62944     this.customEditors = this.customEditors || {};
62945 };
62946 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62947     
62948      /**
62949      * @cfg {Object} customEditors map of colnames=> custom editors.
62950      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62951      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62952      * false disables editing of the field.
62953          */
62954     
62955       /**
62956      * @cfg {Object} propertyNames map of property Names to their displayed value
62957          */
62958     
62959     render : function(){
62960         Roo.grid.PropertyGrid.superclass.render.call(this);
62961         this.autoSize.defer(100, this);
62962     },
62963
62964     autoSize : function(){
62965         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62966         if(this.view){
62967             this.view.fitColumns();
62968         }
62969     },
62970
62971     onColumnResize : function(){
62972         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62973         this.autoSize();
62974     },
62975     /**
62976      * Sets the data for the Grid
62977      * accepts a Key => Value object of all the elements avaiable.
62978      * @param {Object} data  to appear in grid.
62979      */
62980     setSource : function(source){
62981         this.store.setSource(source);
62982         //this.autoSize();
62983     },
62984     /**
62985      * Gets all the data from the grid.
62986      * @return {Object} data  data stored in grid
62987      */
62988     getSource : function(){
62989         return this.store.getSource();
62990     }
62991 });/*
62992   
62993  * Licence LGPL
62994  
62995  */
62996  
62997 /**
62998  * @class Roo.grid.Calendar
62999  * @extends Roo.grid.Grid
63000  * This class extends the Grid to provide a calendar widget
63001  * <br><br>Usage:<pre><code>
63002  var grid = new Roo.grid.Calendar("my-container-id", {
63003      ds: myDataStore,
63004      cm: myColModel,
63005      selModel: mySelectionModel,
63006      autoSizeColumns: true,
63007      monitorWindowResize: false,
63008      trackMouseOver: true
63009      eventstore : real data store..
63010  });
63011  // set any options
63012  grid.render();
63013   
63014   * @constructor
63015  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63016  * The container MUST have some type of size defined for the grid to fill. The container will be
63017  * automatically set to position relative if it isn't already.
63018  * @param {Object} config A config object that sets properties on this grid.
63019  */
63020 Roo.grid.Calendar = function(container, config){
63021         // initialize the container
63022         this.container = Roo.get(container);
63023         this.container.update("");
63024         this.container.setStyle("overflow", "hidden");
63025     this.container.addClass('x-grid-container');
63026
63027     this.id = this.container.id;
63028
63029     Roo.apply(this, config);
63030     // check and correct shorthanded configs
63031     
63032     var rows = [];
63033     var d =1;
63034     for (var r = 0;r < 6;r++) {
63035         
63036         rows[r]=[];
63037         for (var c =0;c < 7;c++) {
63038             rows[r][c]= '';
63039         }
63040     }
63041     if (this.eventStore) {
63042         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63043         this.eventStore.on('load',this.onLoad, this);
63044         this.eventStore.on('beforeload',this.clearEvents, this);
63045          
63046     }
63047     
63048     this.dataSource = new Roo.data.Store({
63049             proxy: new Roo.data.MemoryProxy(rows),
63050             reader: new Roo.data.ArrayReader({}, [
63051                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63052     });
63053
63054     this.dataSource.load();
63055     this.ds = this.dataSource;
63056     this.ds.xmodule = this.xmodule || false;
63057     
63058     
63059     var cellRender = function(v,x,r)
63060     {
63061         return String.format(
63062             '<div class="fc-day  fc-widget-content"><div>' +
63063                 '<div class="fc-event-container"></div>' +
63064                 '<div class="fc-day-number">{0}</div>'+
63065                 
63066                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63067             '</div></div>', v);
63068     
63069     }
63070     
63071     
63072     this.colModel = new Roo.grid.ColumnModel( [
63073         {
63074             xtype: 'ColumnModel',
63075             xns: Roo.grid,
63076             dataIndex : 'weekday0',
63077             header : 'Sunday',
63078             renderer : cellRender
63079         },
63080         {
63081             xtype: 'ColumnModel',
63082             xns: Roo.grid,
63083             dataIndex : 'weekday1',
63084             header : 'Monday',
63085             renderer : cellRender
63086         },
63087         {
63088             xtype: 'ColumnModel',
63089             xns: Roo.grid,
63090             dataIndex : 'weekday2',
63091             header : 'Tuesday',
63092             renderer : cellRender
63093         },
63094         {
63095             xtype: 'ColumnModel',
63096             xns: Roo.grid,
63097             dataIndex : 'weekday3',
63098             header : 'Wednesday',
63099             renderer : cellRender
63100         },
63101         {
63102             xtype: 'ColumnModel',
63103             xns: Roo.grid,
63104             dataIndex : 'weekday4',
63105             header : 'Thursday',
63106             renderer : cellRender
63107         },
63108         {
63109             xtype: 'ColumnModel',
63110             xns: Roo.grid,
63111             dataIndex : 'weekday5',
63112             header : 'Friday',
63113             renderer : cellRender
63114         },
63115         {
63116             xtype: 'ColumnModel',
63117             xns: Roo.grid,
63118             dataIndex : 'weekday6',
63119             header : 'Saturday',
63120             renderer : cellRender
63121         }
63122     ]);
63123     this.cm = this.colModel;
63124     this.cm.xmodule = this.xmodule || false;
63125  
63126         
63127           
63128     //this.selModel = new Roo.grid.CellSelectionModel();
63129     //this.sm = this.selModel;
63130     //this.selModel.init(this);
63131     
63132     
63133     if(this.width){
63134         this.container.setWidth(this.width);
63135     }
63136
63137     if(this.height){
63138         this.container.setHeight(this.height);
63139     }
63140     /** @private */
63141         this.addEvents({
63142         // raw events
63143         /**
63144          * @event click
63145          * The raw click event for the entire grid.
63146          * @param {Roo.EventObject} e
63147          */
63148         "click" : true,
63149         /**
63150          * @event dblclick
63151          * The raw dblclick event for the entire grid.
63152          * @param {Roo.EventObject} e
63153          */
63154         "dblclick" : true,
63155         /**
63156          * @event contextmenu
63157          * The raw contextmenu event for the entire grid.
63158          * @param {Roo.EventObject} e
63159          */
63160         "contextmenu" : true,
63161         /**
63162          * @event mousedown
63163          * The raw mousedown event for the entire grid.
63164          * @param {Roo.EventObject} e
63165          */
63166         "mousedown" : true,
63167         /**
63168          * @event mouseup
63169          * The raw mouseup event for the entire grid.
63170          * @param {Roo.EventObject} e
63171          */
63172         "mouseup" : true,
63173         /**
63174          * @event mouseover
63175          * The raw mouseover event for the entire grid.
63176          * @param {Roo.EventObject} e
63177          */
63178         "mouseover" : true,
63179         /**
63180          * @event mouseout
63181          * The raw mouseout event for the entire grid.
63182          * @param {Roo.EventObject} e
63183          */
63184         "mouseout" : true,
63185         /**
63186          * @event keypress
63187          * The raw keypress event for the entire grid.
63188          * @param {Roo.EventObject} e
63189          */
63190         "keypress" : true,
63191         /**
63192          * @event keydown
63193          * The raw keydown event for the entire grid.
63194          * @param {Roo.EventObject} e
63195          */
63196         "keydown" : true,
63197
63198         // custom events
63199
63200         /**
63201          * @event cellclick
63202          * Fires when a cell is clicked
63203          * @param {Grid} this
63204          * @param {Number} rowIndex
63205          * @param {Number} columnIndex
63206          * @param {Roo.EventObject} e
63207          */
63208         "cellclick" : true,
63209         /**
63210          * @event celldblclick
63211          * Fires when a cell is double clicked
63212          * @param {Grid} this
63213          * @param {Number} rowIndex
63214          * @param {Number} columnIndex
63215          * @param {Roo.EventObject} e
63216          */
63217         "celldblclick" : true,
63218         /**
63219          * @event rowclick
63220          * Fires when a row is clicked
63221          * @param {Grid} this
63222          * @param {Number} rowIndex
63223          * @param {Roo.EventObject} e
63224          */
63225         "rowclick" : true,
63226         /**
63227          * @event rowdblclick
63228          * Fires when a row is double clicked
63229          * @param {Grid} this
63230          * @param {Number} rowIndex
63231          * @param {Roo.EventObject} e
63232          */
63233         "rowdblclick" : true,
63234         /**
63235          * @event headerclick
63236          * Fires when a header is clicked
63237          * @param {Grid} this
63238          * @param {Number} columnIndex
63239          * @param {Roo.EventObject} e
63240          */
63241         "headerclick" : true,
63242         /**
63243          * @event headerdblclick
63244          * Fires when a header cell is double clicked
63245          * @param {Grid} this
63246          * @param {Number} columnIndex
63247          * @param {Roo.EventObject} e
63248          */
63249         "headerdblclick" : true,
63250         /**
63251          * @event rowcontextmenu
63252          * Fires when a row is right clicked
63253          * @param {Grid} this
63254          * @param {Number} rowIndex
63255          * @param {Roo.EventObject} e
63256          */
63257         "rowcontextmenu" : true,
63258         /**
63259          * @event cellcontextmenu
63260          * Fires when a cell is right clicked
63261          * @param {Grid} this
63262          * @param {Number} rowIndex
63263          * @param {Number} cellIndex
63264          * @param {Roo.EventObject} e
63265          */
63266          "cellcontextmenu" : true,
63267         /**
63268          * @event headercontextmenu
63269          * Fires when a header is right clicked
63270          * @param {Grid} this
63271          * @param {Number} columnIndex
63272          * @param {Roo.EventObject} e
63273          */
63274         "headercontextmenu" : true,
63275         /**
63276          * @event bodyscroll
63277          * Fires when the body element is scrolled
63278          * @param {Number} scrollLeft
63279          * @param {Number} scrollTop
63280          */
63281         "bodyscroll" : true,
63282         /**
63283          * @event columnresize
63284          * Fires when the user resizes a column
63285          * @param {Number} columnIndex
63286          * @param {Number} newSize
63287          */
63288         "columnresize" : true,
63289         /**
63290          * @event columnmove
63291          * Fires when the user moves a column
63292          * @param {Number} oldIndex
63293          * @param {Number} newIndex
63294          */
63295         "columnmove" : true,
63296         /**
63297          * @event startdrag
63298          * Fires when row(s) start being dragged
63299          * @param {Grid} this
63300          * @param {Roo.GridDD} dd The drag drop object
63301          * @param {event} e The raw browser event
63302          */
63303         "startdrag" : true,
63304         /**
63305          * @event enddrag
63306          * Fires when a drag operation is complete
63307          * @param {Grid} this
63308          * @param {Roo.GridDD} dd The drag drop object
63309          * @param {event} e The raw browser event
63310          */
63311         "enddrag" : true,
63312         /**
63313          * @event dragdrop
63314          * Fires when dragged row(s) are dropped on a valid DD target
63315          * @param {Grid} this
63316          * @param {Roo.GridDD} dd The drag drop object
63317          * @param {String} targetId The target drag drop object
63318          * @param {event} e The raw browser event
63319          */
63320         "dragdrop" : true,
63321         /**
63322          * @event dragover
63323          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63324          * @param {Grid} this
63325          * @param {Roo.GridDD} dd The drag drop object
63326          * @param {String} targetId The target drag drop object
63327          * @param {event} e The raw browser event
63328          */
63329         "dragover" : true,
63330         /**
63331          * @event dragenter
63332          *  Fires when the dragged row(s) first cross another DD target while being dragged
63333          * @param {Grid} this
63334          * @param {Roo.GridDD} dd The drag drop object
63335          * @param {String} targetId The target drag drop object
63336          * @param {event} e The raw browser event
63337          */
63338         "dragenter" : true,
63339         /**
63340          * @event dragout
63341          * Fires when the dragged row(s) leave another DD target while being dragged
63342          * @param {Grid} this
63343          * @param {Roo.GridDD} dd The drag drop object
63344          * @param {String} targetId The target drag drop object
63345          * @param {event} e The raw browser event
63346          */
63347         "dragout" : true,
63348         /**
63349          * @event rowclass
63350          * Fires when a row is rendered, so you can change add a style to it.
63351          * @param {GridView} gridview   The grid view
63352          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63353          */
63354         'rowclass' : true,
63355
63356         /**
63357          * @event render
63358          * Fires when the grid is rendered
63359          * @param {Grid} grid
63360          */
63361         'render' : true,
63362             /**
63363              * @event select
63364              * Fires when a date is selected
63365              * @param {DatePicker} this
63366              * @param {Date} date The selected date
63367              */
63368         'select': true,
63369         /**
63370              * @event monthchange
63371              * Fires when the displayed month changes 
63372              * @param {DatePicker} this
63373              * @param {Date} date The selected month
63374              */
63375         'monthchange': true,
63376         /**
63377              * @event evententer
63378              * Fires when mouse over an event
63379              * @param {Calendar} this
63380              * @param {event} Event
63381              */
63382         'evententer': true,
63383         /**
63384              * @event eventleave
63385              * Fires when the mouse leaves an
63386              * @param {Calendar} this
63387              * @param {event}
63388              */
63389         'eventleave': true,
63390         /**
63391              * @event eventclick
63392              * Fires when the mouse click an
63393              * @param {Calendar} this
63394              * @param {event}
63395              */
63396         'eventclick': true,
63397         /**
63398              * @event eventrender
63399              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63400              * @param {Calendar} this
63401              * @param {data} data to be modified
63402              */
63403         'eventrender': true
63404         
63405     });
63406
63407     Roo.grid.Grid.superclass.constructor.call(this);
63408     this.on('render', function() {
63409         this.view.el.addClass('x-grid-cal'); 
63410         
63411         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63412
63413     },this);
63414     
63415     if (!Roo.grid.Calendar.style) {
63416         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63417             
63418             
63419             '.x-grid-cal .x-grid-col' :  {
63420                 height: 'auto !important',
63421                 'vertical-align': 'top'
63422             },
63423             '.x-grid-cal  .fc-event-hori' : {
63424                 height: '14px'
63425             }
63426              
63427             
63428         }, Roo.id());
63429     }
63430
63431     
63432     
63433 };
63434 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63435     /**
63436      * @cfg {Store} eventStore The store that loads events.
63437      */
63438     eventStore : 25,
63439
63440      
63441     activeDate : false,
63442     startDay : 0,
63443     autoWidth : true,
63444     monitorWindowResize : false,
63445
63446     
63447     resizeColumns : function() {
63448         var col = (this.view.el.getWidth() / 7) - 3;
63449         // loop through cols, and setWidth
63450         for(var i =0 ; i < 7 ; i++){
63451             this.cm.setColumnWidth(i, col);
63452         }
63453     },
63454      setDate :function(date) {
63455         
63456         Roo.log('setDate?');
63457         
63458         this.resizeColumns();
63459         var vd = this.activeDate;
63460         this.activeDate = date;
63461 //        if(vd && this.el){
63462 //            var t = date.getTime();
63463 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63464 //                Roo.log('using add remove');
63465 //                
63466 //                this.fireEvent('monthchange', this, date);
63467 //                
63468 //                this.cells.removeClass("fc-state-highlight");
63469 //                this.cells.each(function(c){
63470 //                   if(c.dateValue == t){
63471 //                       c.addClass("fc-state-highlight");
63472 //                       setTimeout(function(){
63473 //                            try{c.dom.firstChild.focus();}catch(e){}
63474 //                       }, 50);
63475 //                       return false;
63476 //                   }
63477 //                   return true;
63478 //                });
63479 //                return;
63480 //            }
63481 //        }
63482         
63483         var days = date.getDaysInMonth();
63484         
63485         var firstOfMonth = date.getFirstDateOfMonth();
63486         var startingPos = firstOfMonth.getDay()-this.startDay;
63487         
63488         if(startingPos < this.startDay){
63489             startingPos += 7;
63490         }
63491         
63492         var pm = date.add(Date.MONTH, -1);
63493         var prevStart = pm.getDaysInMonth()-startingPos;
63494 //        
63495         
63496         
63497         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63498         
63499         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63500         //this.cells.addClassOnOver('fc-state-hover');
63501         
63502         var cells = this.cells.elements;
63503         var textEls = this.textNodes;
63504         
63505         //Roo.each(cells, function(cell){
63506         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63507         //});
63508         
63509         days += startingPos;
63510
63511         // convert everything to numbers so it's fast
63512         var day = 86400000;
63513         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63514         //Roo.log(d);
63515         //Roo.log(pm);
63516         //Roo.log(prevStart);
63517         
63518         var today = new Date().clearTime().getTime();
63519         var sel = date.clearTime().getTime();
63520         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63521         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63522         var ddMatch = this.disabledDatesRE;
63523         var ddText = this.disabledDatesText;
63524         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63525         var ddaysText = this.disabledDaysText;
63526         var format = this.format;
63527         
63528         var setCellClass = function(cal, cell){
63529             
63530             //Roo.log('set Cell Class');
63531             cell.title = "";
63532             var t = d.getTime();
63533             
63534             //Roo.log(d);
63535             
63536             
63537             cell.dateValue = t;
63538             if(t == today){
63539                 cell.className += " fc-today";
63540                 cell.className += " fc-state-highlight";
63541                 cell.title = cal.todayText;
63542             }
63543             if(t == sel){
63544                 // disable highlight in other month..
63545                 cell.className += " fc-state-highlight";
63546                 
63547             }
63548             // disabling
63549             if(t < min) {
63550                 //cell.className = " fc-state-disabled";
63551                 cell.title = cal.minText;
63552                 return;
63553             }
63554             if(t > max) {
63555                 //cell.className = " fc-state-disabled";
63556                 cell.title = cal.maxText;
63557                 return;
63558             }
63559             if(ddays){
63560                 if(ddays.indexOf(d.getDay()) != -1){
63561                     // cell.title = ddaysText;
63562                    // cell.className = " fc-state-disabled";
63563                 }
63564             }
63565             if(ddMatch && format){
63566                 var fvalue = d.dateFormat(format);
63567                 if(ddMatch.test(fvalue)){
63568                     cell.title = ddText.replace("%0", fvalue);
63569                    cell.className = " fc-state-disabled";
63570                 }
63571             }
63572             
63573             if (!cell.initialClassName) {
63574                 cell.initialClassName = cell.dom.className;
63575             }
63576             
63577             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63578         };
63579
63580         var i = 0;
63581         
63582         for(; i < startingPos; i++) {
63583             cells[i].dayName =  (++prevStart);
63584             Roo.log(textEls[i]);
63585             d.setDate(d.getDate()+1);
63586             
63587             //cells[i].className = "fc-past fc-other-month";
63588             setCellClass(this, cells[i]);
63589         }
63590         
63591         var intDay = 0;
63592         
63593         for(; i < days; i++){
63594             intDay = i - startingPos + 1;
63595             cells[i].dayName =  (intDay);
63596             d.setDate(d.getDate()+1);
63597             
63598             cells[i].className = ''; // "x-date-active";
63599             setCellClass(this, cells[i]);
63600         }
63601         var extraDays = 0;
63602         
63603         for(; i < 42; i++) {
63604             //textEls[i].innerHTML = (++extraDays);
63605             
63606             d.setDate(d.getDate()+1);
63607             cells[i].dayName = (++extraDays);
63608             cells[i].className = "fc-future fc-other-month";
63609             setCellClass(this, cells[i]);
63610         }
63611         
63612         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63613         
63614         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63615         
63616         // this will cause all the cells to mis
63617         var rows= [];
63618         var i =0;
63619         for (var r = 0;r < 6;r++) {
63620             for (var c =0;c < 7;c++) {
63621                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63622             }    
63623         }
63624         
63625         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63626         for(i=0;i<cells.length;i++) {
63627             
63628             this.cells.elements[i].dayName = cells[i].dayName ;
63629             this.cells.elements[i].className = cells[i].className;
63630             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63631             this.cells.elements[i].title = cells[i].title ;
63632             this.cells.elements[i].dateValue = cells[i].dateValue ;
63633         }
63634         
63635         
63636         
63637         
63638         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63639         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63640         
63641         ////if(totalRows != 6){
63642             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63643            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63644        // }
63645         
63646         this.fireEvent('monthchange', this, date);
63647         
63648         
63649     },
63650  /**
63651      * Returns the grid's SelectionModel.
63652      * @return {SelectionModel}
63653      */
63654     getSelectionModel : function(){
63655         if(!this.selModel){
63656             this.selModel = new Roo.grid.CellSelectionModel();
63657         }
63658         return this.selModel;
63659     },
63660
63661     load: function() {
63662         this.eventStore.load()
63663         
63664         
63665         
63666     },
63667     
63668     findCell : function(dt) {
63669         dt = dt.clearTime().getTime();
63670         var ret = false;
63671         this.cells.each(function(c){
63672             //Roo.log("check " +c.dateValue + '?=' + dt);
63673             if(c.dateValue == dt){
63674                 ret = c;
63675                 return false;
63676             }
63677             return true;
63678         });
63679         
63680         return ret;
63681     },
63682     
63683     findCells : function(rec) {
63684         var s = rec.data.start_dt.clone().clearTime().getTime();
63685        // Roo.log(s);
63686         var e= rec.data.end_dt.clone().clearTime().getTime();
63687        // Roo.log(e);
63688         var ret = [];
63689         this.cells.each(function(c){
63690              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63691             
63692             if(c.dateValue > e){
63693                 return ;
63694             }
63695             if(c.dateValue < s){
63696                 return ;
63697             }
63698             ret.push(c);
63699         });
63700         
63701         return ret;    
63702     },
63703     
63704     findBestRow: function(cells)
63705     {
63706         var ret = 0;
63707         
63708         for (var i =0 ; i < cells.length;i++) {
63709             ret  = Math.max(cells[i].rows || 0,ret);
63710         }
63711         return ret;
63712         
63713     },
63714     
63715     
63716     addItem : function(rec)
63717     {
63718         // look for vertical location slot in
63719         var cells = this.findCells(rec);
63720         
63721         rec.row = this.findBestRow(cells);
63722         
63723         // work out the location.
63724         
63725         var crow = false;
63726         var rows = [];
63727         for(var i =0; i < cells.length; i++) {
63728             if (!crow) {
63729                 crow = {
63730                     start : cells[i],
63731                     end :  cells[i]
63732                 };
63733                 continue;
63734             }
63735             if (crow.start.getY() == cells[i].getY()) {
63736                 // on same row.
63737                 crow.end = cells[i];
63738                 continue;
63739             }
63740             // different row.
63741             rows.push(crow);
63742             crow = {
63743                 start: cells[i],
63744                 end : cells[i]
63745             };
63746             
63747         }
63748         
63749         rows.push(crow);
63750         rec.els = [];
63751         rec.rows = rows;
63752         rec.cells = cells;
63753         for (var i = 0; i < cells.length;i++) {
63754             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63755             
63756         }
63757         
63758         
63759     },
63760     
63761     clearEvents: function() {
63762         
63763         if (!this.eventStore.getCount()) {
63764             return;
63765         }
63766         // reset number of rows in cells.
63767         Roo.each(this.cells.elements, function(c){
63768             c.rows = 0;
63769         });
63770         
63771         this.eventStore.each(function(e) {
63772             this.clearEvent(e);
63773         },this);
63774         
63775     },
63776     
63777     clearEvent : function(ev)
63778     {
63779         if (ev.els) {
63780             Roo.each(ev.els, function(el) {
63781                 el.un('mouseenter' ,this.onEventEnter, this);
63782                 el.un('mouseleave' ,this.onEventLeave, this);
63783                 el.remove();
63784             },this);
63785             ev.els = [];
63786         }
63787     },
63788     
63789     
63790     renderEvent : function(ev,ctr) {
63791         if (!ctr) {
63792              ctr = this.view.el.select('.fc-event-container',true).first();
63793         }
63794         
63795          
63796         this.clearEvent(ev);
63797             //code
63798        
63799         
63800         
63801         ev.els = [];
63802         var cells = ev.cells;
63803         var rows = ev.rows;
63804         this.fireEvent('eventrender', this, ev);
63805         
63806         for(var i =0; i < rows.length; i++) {
63807             
63808             cls = '';
63809             if (i == 0) {
63810                 cls += ' fc-event-start';
63811             }
63812             if ((i+1) == rows.length) {
63813                 cls += ' fc-event-end';
63814             }
63815             
63816             //Roo.log(ev.data);
63817             // how many rows should it span..
63818             var cg = this.eventTmpl.append(ctr,Roo.apply({
63819                 fccls : cls
63820                 
63821             }, ev.data) , true);
63822             
63823             
63824             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63825             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63826             cg.on('click', this.onEventClick, this, ev);
63827             
63828             ev.els.push(cg);
63829             
63830             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63831             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63832             //Roo.log(cg);
63833              
63834             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63835             cg.setWidth(ebox.right - sbox.x -2);
63836         }
63837     },
63838     
63839     renderEvents: function()
63840     {   
63841         // first make sure there is enough space..
63842         
63843         if (!this.eventTmpl) {
63844             this.eventTmpl = new Roo.Template(
63845                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63846                     '<div class="fc-event-inner">' +
63847                         '<span class="fc-event-time">{time}</span>' +
63848                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63849                     '</div>' +
63850                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63851                 '</div>'
63852             );
63853                 
63854         }
63855                
63856         
63857         
63858         this.cells.each(function(c) {
63859             //Roo.log(c.select('.fc-day-content div',true).first());
63860             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63861         });
63862         
63863         var ctr = this.view.el.select('.fc-event-container',true).first();
63864         
63865         var cls;
63866         this.eventStore.each(function(ev){
63867             
63868             this.renderEvent(ev);
63869              
63870              
63871         }, this);
63872         this.view.layout();
63873         
63874     },
63875     
63876     onEventEnter: function (e, el,event,d) {
63877         this.fireEvent('evententer', this, el, event);
63878     },
63879     
63880     onEventLeave: function (e, el,event,d) {
63881         this.fireEvent('eventleave', this, el, event);
63882     },
63883     
63884     onEventClick: function (e, el,event,d) {
63885         this.fireEvent('eventclick', this, el, event);
63886     },
63887     
63888     onMonthChange: function () {
63889         this.store.load();
63890     },
63891     
63892     onLoad: function () {
63893         
63894         //Roo.log('calendar onload');
63895 //         
63896         if(this.eventStore.getCount() > 0){
63897             
63898            
63899             
63900             this.eventStore.each(function(d){
63901                 
63902                 
63903                 // FIXME..
63904                 var add =   d.data;
63905                 if (typeof(add.end_dt) == 'undefined')  {
63906                     Roo.log("Missing End time in calendar data: ");
63907                     Roo.log(d);
63908                     return;
63909                 }
63910                 if (typeof(add.start_dt) == 'undefined')  {
63911                     Roo.log("Missing Start time in calendar data: ");
63912                     Roo.log(d);
63913                     return;
63914                 }
63915                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63916                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63917                 add.id = add.id || d.id;
63918                 add.title = add.title || '??';
63919                 
63920                 this.addItem(d);
63921                 
63922              
63923             },this);
63924         }
63925         
63926         this.renderEvents();
63927     }
63928     
63929
63930 });
63931 /*
63932  grid : {
63933                 xtype: 'Grid',
63934                 xns: Roo.grid,
63935                 listeners : {
63936                     render : function ()
63937                     {
63938                         _this.grid = this;
63939                         
63940                         if (!this.view.el.hasClass('course-timesheet')) {
63941                             this.view.el.addClass('course-timesheet');
63942                         }
63943                         if (this.tsStyle) {
63944                             this.ds.load({});
63945                             return; 
63946                         }
63947                         Roo.log('width');
63948                         Roo.log(_this.grid.view.el.getWidth());
63949                         
63950                         
63951                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63952                             '.course-timesheet .x-grid-row' : {
63953                                 height: '80px'
63954                             },
63955                             '.x-grid-row td' : {
63956                                 'vertical-align' : 0
63957                             },
63958                             '.course-edit-link' : {
63959                                 'color' : 'blue',
63960                                 'text-overflow' : 'ellipsis',
63961                                 'overflow' : 'hidden',
63962                                 'white-space' : 'nowrap',
63963                                 'cursor' : 'pointer'
63964                             },
63965                             '.sub-link' : {
63966                                 'color' : 'green'
63967                             },
63968                             '.de-act-sup-link' : {
63969                                 'color' : 'purple',
63970                                 'text-decoration' : 'line-through'
63971                             },
63972                             '.de-act-link' : {
63973                                 'color' : 'red',
63974                                 'text-decoration' : 'line-through'
63975                             },
63976                             '.course-timesheet .course-highlight' : {
63977                                 'border-top-style': 'dashed !important',
63978                                 'border-bottom-bottom': 'dashed !important'
63979                             },
63980                             '.course-timesheet .course-item' : {
63981                                 'font-family'   : 'tahoma, arial, helvetica',
63982                                 'font-size'     : '11px',
63983                                 'overflow'      : 'hidden',
63984                                 'padding-left'  : '10px',
63985                                 'padding-right' : '10px',
63986                                 'padding-top' : '10px' 
63987                             }
63988                             
63989                         }, Roo.id());
63990                                 this.ds.load({});
63991                     }
63992                 },
63993                 autoWidth : true,
63994                 monitorWindowResize : false,
63995                 cellrenderer : function(v,x,r)
63996                 {
63997                     return v;
63998                 },
63999                 sm : {
64000                     xtype: 'CellSelectionModel',
64001                     xns: Roo.grid
64002                 },
64003                 dataSource : {
64004                     xtype: 'Store',
64005                     xns: Roo.data,
64006                     listeners : {
64007                         beforeload : function (_self, options)
64008                         {
64009                             options.params = options.params || {};
64010                             options.params._month = _this.monthField.getValue();
64011                             options.params.limit = 9999;
64012                             options.params['sort'] = 'when_dt';    
64013                             options.params['dir'] = 'ASC';    
64014                             this.proxy.loadResponse = this.loadResponse;
64015                             Roo.log("load?");
64016                             //this.addColumns();
64017                         },
64018                         load : function (_self, records, options)
64019                         {
64020                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64021                                 // if you click on the translation.. you can edit it...
64022                                 var el = Roo.get(this);
64023                                 var id = el.dom.getAttribute('data-id');
64024                                 var d = el.dom.getAttribute('data-date');
64025                                 var t = el.dom.getAttribute('data-time');
64026                                 //var id = this.child('span').dom.textContent;
64027                                 
64028                                 //Roo.log(this);
64029                                 Pman.Dialog.CourseCalendar.show({
64030                                     id : id,
64031                                     when_d : d,
64032                                     when_t : t,
64033                                     productitem_active : id ? 1 : 0
64034                                 }, function() {
64035                                     _this.grid.ds.load({});
64036                                 });
64037                            
64038                            });
64039                            
64040                            _this.panel.fireEvent('resize', [ '', '' ]);
64041                         }
64042                     },
64043                     loadResponse : function(o, success, response){
64044                             // this is overridden on before load..
64045                             
64046                             Roo.log("our code?");       
64047                             //Roo.log(success);
64048                             //Roo.log(response)
64049                             delete this.activeRequest;
64050                             if(!success){
64051                                 this.fireEvent("loadexception", this, o, response);
64052                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64053                                 return;
64054                             }
64055                             var result;
64056                             try {
64057                                 result = o.reader.read(response);
64058                             }catch(e){
64059                                 Roo.log("load exception?");
64060                                 this.fireEvent("loadexception", this, o, response, e);
64061                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64062                                 return;
64063                             }
64064                             Roo.log("ready...");        
64065                             // loop through result.records;
64066                             // and set this.tdate[date] = [] << array of records..
64067                             _this.tdata  = {};
64068                             Roo.each(result.records, function(r){
64069                                 //Roo.log(r.data);
64070                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64071                                     _this.tdata[r.data.when_dt.format('j')] = [];
64072                                 }
64073                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64074                             });
64075                             
64076                             //Roo.log(_this.tdata);
64077                             
64078                             result.records = [];
64079                             result.totalRecords = 6;
64080                     
64081                             // let's generate some duumy records for the rows.
64082                             //var st = _this.dateField.getValue();
64083                             
64084                             // work out monday..
64085                             //st = st.add(Date.DAY, -1 * st.format('w'));
64086                             
64087                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64088                             
64089                             var firstOfMonth = date.getFirstDayOfMonth();
64090                             var days = date.getDaysInMonth();
64091                             var d = 1;
64092                             var firstAdded = false;
64093                             for (var i = 0; i < result.totalRecords ; i++) {
64094                                 //var d= st.add(Date.DAY, i);
64095                                 var row = {};
64096                                 var added = 0;
64097                                 for(var w = 0 ; w < 7 ; w++){
64098                                     if(!firstAdded && firstOfMonth != w){
64099                                         continue;
64100                                     }
64101                                     if(d > days){
64102                                         continue;
64103                                     }
64104                                     firstAdded = true;
64105                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64106                                     row['weekday'+w] = String.format(
64107                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64108                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64109                                                     d,
64110                                                     date.format('Y-m-')+dd
64111                                                 );
64112                                     added++;
64113                                     if(typeof(_this.tdata[d]) != 'undefined'){
64114                                         Roo.each(_this.tdata[d], function(r){
64115                                             var is_sub = '';
64116                                             var deactive = '';
64117                                             var id = r.id;
64118                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64119                                             if(r.parent_id*1>0){
64120                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64121                                                 id = r.parent_id;
64122                                             }
64123                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64124                                                 deactive = 'de-act-link';
64125                                             }
64126                                             
64127                                             row['weekday'+w] += String.format(
64128                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64129                                                     id, //0
64130                                                     r.product_id_name, //1
64131                                                     r.when_dt.format('h:ia'), //2
64132                                                     is_sub, //3
64133                                                     deactive, //4
64134                                                     desc // 5
64135                                             );
64136                                         });
64137                                     }
64138                                     d++;
64139                                 }
64140                                 
64141                                 // only do this if something added..
64142                                 if(added > 0){ 
64143                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64144                                 }
64145                                 
64146                                 
64147                                 // push it twice. (second one with an hour..
64148                                 
64149                             }
64150                             //Roo.log(result);
64151                             this.fireEvent("load", this, o, o.request.arg);
64152                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64153                         },
64154                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64155                     proxy : {
64156                         xtype: 'HttpProxy',
64157                         xns: Roo.data,
64158                         method : 'GET',
64159                         url : baseURL + '/Roo/Shop_course.php'
64160                     },
64161                     reader : {
64162                         xtype: 'JsonReader',
64163                         xns: Roo.data,
64164                         id : 'id',
64165                         fields : [
64166                             {
64167                                 'name': 'id',
64168                                 'type': 'int'
64169                             },
64170                             {
64171                                 'name': 'when_dt',
64172                                 'type': 'string'
64173                             },
64174                             {
64175                                 'name': 'end_dt',
64176                                 'type': 'string'
64177                             },
64178                             {
64179                                 'name': 'parent_id',
64180                                 'type': 'int'
64181                             },
64182                             {
64183                                 'name': 'product_id',
64184                                 'type': 'int'
64185                             },
64186                             {
64187                                 'name': 'productitem_id',
64188                                 'type': 'int'
64189                             },
64190                             {
64191                                 'name': 'guid',
64192                                 'type': 'int'
64193                             }
64194                         ]
64195                     }
64196                 },
64197                 toolbar : {
64198                     xtype: 'Toolbar',
64199                     xns: Roo,
64200                     items : [
64201                         {
64202                             xtype: 'Button',
64203                             xns: Roo.Toolbar,
64204                             listeners : {
64205                                 click : function (_self, e)
64206                                 {
64207                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64208                                     sd.setMonth(sd.getMonth()-1);
64209                                     _this.monthField.setValue(sd.format('Y-m-d'));
64210                                     _this.grid.ds.load({});
64211                                 }
64212                             },
64213                             text : "Back"
64214                         },
64215                         {
64216                             xtype: 'Separator',
64217                             xns: Roo.Toolbar
64218                         },
64219                         {
64220                             xtype: 'MonthField',
64221                             xns: Roo.form,
64222                             listeners : {
64223                                 render : function (_self)
64224                                 {
64225                                     _this.monthField = _self;
64226                                    // _this.monthField.set  today
64227                                 },
64228                                 select : function (combo, date)
64229                                 {
64230                                     _this.grid.ds.load({});
64231                                 }
64232                             },
64233                             value : (function() { return new Date(); })()
64234                         },
64235                         {
64236                             xtype: 'Separator',
64237                             xns: Roo.Toolbar
64238                         },
64239                         {
64240                             xtype: 'TextItem',
64241                             xns: Roo.Toolbar,
64242                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64243                         },
64244                         {
64245                             xtype: 'Fill',
64246                             xns: Roo.Toolbar
64247                         },
64248                         {
64249                             xtype: 'Button',
64250                             xns: Roo.Toolbar,
64251                             listeners : {
64252                                 click : function (_self, e)
64253                                 {
64254                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64255                                     sd.setMonth(sd.getMonth()+1);
64256                                     _this.monthField.setValue(sd.format('Y-m-d'));
64257                                     _this.grid.ds.load({});
64258                                 }
64259                             },
64260                             text : "Next"
64261                         }
64262                     ]
64263                 },
64264                  
64265             }
64266         };
64267         
64268         *//*
64269  * Based on:
64270  * Ext JS Library 1.1.1
64271  * Copyright(c) 2006-2007, Ext JS, LLC.
64272  *
64273  * Originally Released Under LGPL - original licence link has changed is not relivant.
64274  *
64275  * Fork - LGPL
64276  * <script type="text/javascript">
64277  */
64278  
64279 /**
64280  * @class Roo.LoadMask
64281  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64282  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64283  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64284  * element's UpdateManager load indicator and will be destroyed after the initial load.
64285  * @constructor
64286  * Create a new LoadMask
64287  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64288  * @param {Object} config The config object
64289  */
64290 Roo.LoadMask = function(el, config){
64291     this.el = Roo.get(el);
64292     Roo.apply(this, config);
64293     if(this.store){
64294         this.store.on('beforeload', this.onBeforeLoad, this);
64295         this.store.on('load', this.onLoad, this);
64296         this.store.on('loadexception', this.onLoadException, this);
64297         this.removeMask = false;
64298     }else{
64299         var um = this.el.getUpdateManager();
64300         um.showLoadIndicator = false; // disable the default indicator
64301         um.on('beforeupdate', this.onBeforeLoad, this);
64302         um.on('update', this.onLoad, this);
64303         um.on('failure', this.onLoad, this);
64304         this.removeMask = true;
64305     }
64306 };
64307
64308 Roo.LoadMask.prototype = {
64309     /**
64310      * @cfg {Boolean} removeMask
64311      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64312      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64313      */
64314     removeMask : false,
64315     /**
64316      * @cfg {String} msg
64317      * The text to display in a centered loading message box (defaults to 'Loading...')
64318      */
64319     msg : 'Loading...',
64320     /**
64321      * @cfg {String} msgCls
64322      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64323      */
64324     msgCls : 'x-mask-loading',
64325
64326     /**
64327      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64328      * @type Boolean
64329      */
64330     disabled: false,
64331
64332     /**
64333      * Disables the mask to prevent it from being displayed
64334      */
64335     disable : function(){
64336        this.disabled = true;
64337     },
64338
64339     /**
64340      * Enables the mask so that it can be displayed
64341      */
64342     enable : function(){
64343         this.disabled = false;
64344     },
64345     
64346     onLoadException : function()
64347     {
64348         Roo.log(arguments);
64349         
64350         if (typeof(arguments[3]) != 'undefined') {
64351             Roo.MessageBox.alert("Error loading",arguments[3]);
64352         } 
64353         /*
64354         try {
64355             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64356                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64357             }   
64358         } catch(e) {
64359             
64360         }
64361         */
64362     
64363         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64364     },
64365     // private
64366     onLoad : function()
64367     {
64368         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64369     },
64370
64371     // private
64372     onBeforeLoad : function(){
64373         if(!this.disabled){
64374             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64375         }
64376     },
64377
64378     // private
64379     destroy : function(){
64380         if(this.store){
64381             this.store.un('beforeload', this.onBeforeLoad, this);
64382             this.store.un('load', this.onLoad, this);
64383             this.store.un('loadexception', this.onLoadException, this);
64384         }else{
64385             var um = this.el.getUpdateManager();
64386             um.un('beforeupdate', this.onBeforeLoad, this);
64387             um.un('update', this.onLoad, this);
64388             um.un('failure', this.onLoad, this);
64389         }
64390     }
64391 };/*
64392  * Based on:
64393  * Ext JS Library 1.1.1
64394  * Copyright(c) 2006-2007, Ext JS, LLC.
64395  *
64396  * Originally Released Under LGPL - original licence link has changed is not relivant.
64397  *
64398  * Fork - LGPL
64399  * <script type="text/javascript">
64400  */
64401
64402
64403 /**
64404  * @class Roo.XTemplate
64405  * @extends Roo.Template
64406  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64407 <pre><code>
64408 var t = new Roo.XTemplate(
64409         '&lt;select name="{name}"&gt;',
64410                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64411         '&lt;/select&gt;'
64412 );
64413  
64414 // then append, applying the master template values
64415  </code></pre>
64416  *
64417  * Supported features:
64418  *
64419  *  Tags:
64420
64421 <pre><code>
64422       {a_variable} - output encoded.
64423       {a_variable.format:("Y-m-d")} - call a method on the variable
64424       {a_variable:raw} - unencoded output
64425       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64426       {a_variable:this.method_on_template(...)} - call a method on the template object.
64427  
64428 </code></pre>
64429  *  The tpl tag:
64430 <pre><code>
64431         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64432         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64433         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64434         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64435   
64436         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64437         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64438 </code></pre>
64439  *      
64440  */
64441 Roo.XTemplate = function()
64442 {
64443     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64444     if (this.html) {
64445         this.compile();
64446     }
64447 };
64448
64449
64450 Roo.extend(Roo.XTemplate, Roo.Template, {
64451
64452     /**
64453      * The various sub templates
64454      */
64455     tpls : false,
64456     /**
64457      *
64458      * basic tag replacing syntax
64459      * WORD:WORD()
64460      *
64461      * // you can fake an object call by doing this
64462      *  x.t:(test,tesT) 
64463      * 
64464      */
64465     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64466
64467     /**
64468      * compile the template
64469      *
64470      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64471      *
64472      */
64473     compile: function()
64474     {
64475         var s = this.html;
64476      
64477         s = ['<tpl>', s, '</tpl>'].join('');
64478     
64479         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64480             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64481             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64482             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64483             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64484             m,
64485             id     = 0,
64486             tpls   = [];
64487     
64488         while(true == !!(m = s.match(re))){
64489             var forMatch   = m[0].match(nameRe),
64490                 ifMatch   = m[0].match(ifRe),
64491                 execMatch   = m[0].match(execRe),
64492                 namedMatch   = m[0].match(namedRe),
64493                 
64494                 exp  = null, 
64495                 fn   = null,
64496                 exec = null,
64497                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64498                 
64499             if (ifMatch) {
64500                 // if - puts fn into test..
64501                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64502                 if(exp){
64503                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64504                 }
64505             }
64506             
64507             if (execMatch) {
64508                 // exec - calls a function... returns empty if true is  returned.
64509                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64510                 if(exp){
64511                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64512                 }
64513             }
64514             
64515             
64516             if (name) {
64517                 // for = 
64518                 switch(name){
64519                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64520                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64521                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64522                 }
64523             }
64524             var uid = namedMatch ? namedMatch[1] : id;
64525             
64526             
64527             tpls.push({
64528                 id:     namedMatch ? namedMatch[1] : id,
64529                 target: name,
64530                 exec:   exec,
64531                 test:   fn,
64532                 body:   m[1] || ''
64533             });
64534             if (namedMatch) {
64535                 s = s.replace(m[0], '');
64536             } else { 
64537                 s = s.replace(m[0], '{xtpl'+ id + '}');
64538             }
64539             ++id;
64540         }
64541         this.tpls = [];
64542         for(var i = tpls.length-1; i >= 0; --i){
64543             this.compileTpl(tpls[i]);
64544             this.tpls[tpls[i].id] = tpls[i];
64545         }
64546         this.master = tpls[tpls.length-1];
64547         return this;
64548     },
64549     /**
64550      * same as applyTemplate, except it's done to one of the subTemplates
64551      * when using named templates, you can do:
64552      *
64553      * var str = pl.applySubTemplate('your-name', values);
64554      *
64555      * 
64556      * @param {Number} id of the template
64557      * @param {Object} values to apply to template
64558      * @param {Object} parent (normaly the instance of this object)
64559      */
64560     applySubTemplate : function(id, values, parent)
64561     {
64562         
64563         
64564         var t = this.tpls[id];
64565         
64566         
64567         try { 
64568             if(t.test && !t.test.call(this, values, parent)){
64569                 return '';
64570             }
64571         } catch(e) {
64572             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64573             Roo.log(e.toString());
64574             Roo.log(t.test);
64575             return ''
64576         }
64577         try { 
64578             
64579             if(t.exec && t.exec.call(this, values, parent)){
64580                 return '';
64581             }
64582         } catch(e) {
64583             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64584             Roo.log(e.toString());
64585             Roo.log(t.exec);
64586             return ''
64587         }
64588         try {
64589             var vs = t.target ? t.target.call(this, values, parent) : values;
64590             parent = t.target ? values : parent;
64591             if(t.target && vs instanceof Array){
64592                 var buf = [];
64593                 for(var i = 0, len = vs.length; i < len; i++){
64594                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64595                 }
64596                 return buf.join('');
64597             }
64598             return t.compiled.call(this, vs, parent);
64599         } catch (e) {
64600             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64601             Roo.log(e.toString());
64602             Roo.log(t.compiled);
64603             return '';
64604         }
64605     },
64606
64607     compileTpl : function(tpl)
64608     {
64609         var fm = Roo.util.Format;
64610         var useF = this.disableFormats !== true;
64611         var sep = Roo.isGecko ? "+" : ",";
64612         var undef = function(str) {
64613             Roo.log("Property not found :"  + str);
64614             return '';
64615         };
64616         
64617         var fn = function(m, name, format, args)
64618         {
64619             //Roo.log(arguments);
64620             args = args ? args.replace(/\\'/g,"'") : args;
64621             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64622             if (typeof(format) == 'undefined') {
64623                 format= 'htmlEncode';
64624             }
64625             if (format == 'raw' ) {
64626                 format = false;
64627             }
64628             
64629             if(name.substr(0, 4) == 'xtpl'){
64630                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64631             }
64632             
64633             // build an array of options to determine if value is undefined..
64634             
64635             // basically get 'xxxx.yyyy' then do
64636             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64637             //    (function () { Roo.log("Property not found"); return ''; })() :
64638             //    ......
64639             
64640             var udef_ar = [];
64641             var lookfor = '';
64642             Roo.each(name.split('.'), function(st) {
64643                 lookfor += (lookfor.length ? '.': '') + st;
64644                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64645             });
64646             
64647             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64648             
64649             
64650             if(format && useF){
64651                 
64652                 args = args ? ',' + args : "";
64653                  
64654                 if(format.substr(0, 5) != "this."){
64655                     format = "fm." + format + '(';
64656                 }else{
64657                     format = 'this.call("'+ format.substr(5) + '", ';
64658                     args = ", values";
64659                 }
64660                 
64661                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64662             }
64663              
64664             if (args.length) {
64665                 // called with xxyx.yuu:(test,test)
64666                 // change to ()
64667                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64668             }
64669             // raw.. - :raw modifier..
64670             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64671             
64672         };
64673         var body;
64674         // branched to use + in gecko and [].join() in others
64675         if(Roo.isGecko){
64676             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64677                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64678                     "';};};";
64679         }else{
64680             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64681             body.push(tpl.body.replace(/(\r\n|\n)/g,
64682                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64683             body.push("'].join('');};};");
64684             body = body.join('');
64685         }
64686         
64687         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64688        
64689         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64690         eval(body);
64691         
64692         return this;
64693     },
64694
64695     applyTemplate : function(values){
64696         return this.master.compiled.call(this, values, {});
64697         //var s = this.subs;
64698     },
64699
64700     apply : function(){
64701         return this.applyTemplate.apply(this, arguments);
64702     }
64703
64704  });
64705
64706 Roo.XTemplate.from = function(el){
64707     el = Roo.getDom(el);
64708     return new Roo.XTemplate(el.value || el.innerHTML);
64709 };